pax_global_header00006660000000000000000000000064132142233500014505gustar00rootroot0000000000000052 comment=123770f9f2e54f2bdb599af5718a74ef4721acf1 casacore-2.4.1/000077500000000000000000000000001321422335000132715ustar00rootroot00000000000000casacore-2.4.1/.gitignore000066400000000000000000000001311321422335000152540ustar00rootroot00000000000000doc/html/ build/ *~ *table.lock *CMakeCache.txt *CMakeFiles .project .cproject .settings casacore-2.4.1/.travis.yml000066400000000000000000000025541321422335000154100ustar00rootroot00000000000000language: cpp cache: ccache env: global: - CCACHE_COMPRESS=1 - CCACHE_CPP2=yes matrix: include: - os: linux compiler: gcc env: CXX11=True - os: linux compiler: clang env: CXX11=True - os: linux compiler: gcc env: CXX11=False - os: osx compiler: clang env: CXX11=True PYTHONVERSION=2.7 - os: osx compiler: clang env: CXX11=True PYTHONVERSION=3.4 sudo: false install: - ./.travis/install.sh addons: apt: packages: - ccache - cmake - flex - bison - libblas-dev - liblapack-dev - libcfitsio3-dev - wcslib-dev - libfftw3-dev - gfortran - libncurses5-dev - libreadline6-dev - libhdf5-serial-dev - libboost-dev - libboost-python-dev - python-numpy - python3-numpy before_script: - ./.travis/before_script.sh script: - cd ${TRAVIS_BUILD_DIR}/build - ccache -s - ccache -z - make -j2 - if [ "$TRAVIS_OS_NAME" = osx ]; then TESTS_TO_SKIP="tConvert$"; else TESTS_TO_SKIP=""; fi # Skip tConvert on OSX; boost-python can't be found on non-standard location - env CTEST_OUTPUT_ON_FAILURE=1 ctest -E ${TESTS_TO_SKIP} - make install - ccache -s notifications: webhooks: urls: - https://webhooks.gitter.im/e/a21dbde2d416245fd698 on_success: always on_failure: always on_start: false casacore-2.4.1/.travis/000077500000000000000000000000001321422335000146575ustar00rootroot00000000000000casacore-2.4.1/.travis/before_script.sh000077500000000000000000000035571321422335000200560ustar00rootroot00000000000000#!/bin/bash set -e set -x wget http://www.iausofa.org/2015_0209_F/sofa_f-20150209_a.tar.gz -O /tmp/sofa.tgz tar -xzf /tmp/sofa.tgz cd sofa/20150209_a/f77/src/ && make && make test && cd ../../../../ mkdir build cd build wget ftp://ftp.astron.nl/outgoing/Measures/WSRT_Measures.ztar tar zxvf WSRT_Measures.ztar # no extern templates makes the build a lot larger if [ "$CXX11" = "False" ]; then ccache -M 160M else ccache -M 80M fi if [ "$TRAVIS_OS_NAME" = osx ]; then PATH=$HOME/miniconda/bin:$PATH source activate testenv PYTHON2_EXECUTABLE=$HOME/miniconda/bin/python PYTHON3_EXECUTABLE=$HOME/miniconda/bin/python which python python -c "import numpy as n; print (n.__version__); print(n.get_include());" export PYTHONPATH=/Users/travis/miniconda/envs/testenv/lib/python${PYTHONVERSION}/site-packages:$PYTHONPATH if [ "$PYTHONVERSION" = "2.7" ]; then BUILD_PYTHON=On BUILD_PYTHON3=Off else ln -s /Users/travis/miniconda/envs/testenv/lib/libboost_python-mt.dylib /Users/travis/miniconda/envs/testenv/lib/libboost_python3-mt.dylib BUILD_PYTHON=Off BUILD_PYTHON3=On fi CMAKE_PREFIX_PATH=/Users/travis/miniconda/envs/testenv/lib ls /Users/travis/miniconda/envs/testenv/lib else PYTHON2_EXECUTABLE=/usr/bin/python2.7 PYTHON3_EXECUTABLE=/usr/bin/python3.4 BUILD_PYTHON=On BUILD_PYTHON3=On CMAKE_PREFIX_PATH= fi echo $PYTHONPATH echo $PATH CXX="ccache $CXX" cmake .. \ -DUSE_FFTW3=ON \ -DBUILD_TESTING=ON \ -DUSE_OPENMP=OFF \ -DUSE_HDF5=ON \ -DBUILD_PYTHON=${BUILD_PYTHON} \ -DBUILD_PYTHON3=${BUILD_PYTHON3} \ -DCXX11=${CXX11} \ -DPYTHON2_EXECUTABLE=${PYTHON2_EXECUTABLE} \ -DPYTHON3_EXECUTABLE=${PYTHON3_EXECUTABLE} \ -DCMAKE_PREFIX_PATH=${CMAKE_PREFIX_PATH} \ -DDATA_DIR=$PWD \ -DSOFA_ROOT_DIR=$HOME \ -DUseCasacoreNamespace=True \ -DCMAKE_INSTALL_PREFIX=${TRAVIS_BUILD_DIR}/installed casacore-2.4.1/.travis/install.sh000077500000000000000000000012761321422335000166720ustar00rootroot00000000000000#!/bin/bash set -e set -x if [ "$TRAVIS_OS_NAME" = osx ]; then MAJORPYTHONVERSION=$(echo $PYTHONVERSION | head -c 1) wget https://repo.continuum.io/miniconda/Miniconda${MAJORPYTHONVERSION}-latest-MacOSX-x86_64.sh -O miniconda.sh; brew update >/dev/null brew tap homebrew/science brew install cfitsio wcslib fftw hdf5 ccache bash miniconda.sh -b -p $HOME/miniconda export PATH="$HOME/miniconda/bin:$PATH" hash -r conda config --set always_yes yes --set changeps1 no conda update -q conda conda config --add channels conda-forge conda info -a conda create -q -n testenv python=${PYTHONVERSION} numpy source activate testenv conda install -q -y -c meznom boost-python fi casacore-2.4.1/CHANGES.md000066400000000000000000000112741321422335000146700ustar00rootroot00000000000000# 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-2.4.1/CMakeLists.txt000066400000000000000000000565231321422335000160440ustar00rootroot00000000000000# # Casacore # project(casacore) cmake_minimum_required (VERSION 2.6.0) include(CheckCXXCompilerFlag) include(CheckCCompilerFlag) include(CheckFunctionExists) # fixes warnings on cmake 3.x+ For now we want the old behavior to be backwards compatible if (POLICY CMP0048) cmake_policy (SET CMP0048 OLD) endif() set(PROJECT_VERSION_MAJOR 2) set(PROJECT_VERSION_MINOR 4) set(PROJECT_VERSION_PATCH 0) set(PROJECT_VERSION_REVISION 1) set(PROJECT_VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}") 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 "casacore.googlecode.com") set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Astronomical data processing library") string(TOLOWER "${CPACK_PACKAGE_NAME}" CPACK_PACKAGE_NAME_LOWERCASE) find_program(DPKG_PROGRAM dpkg DOC "dpkg program of Debian-based systems") if(DPKG_PROGRAM) execute_process( COMMAND ${DPKG_PROGRAM} --print-architecture OUTPUT_VARIABLE CPACK_DEBIAN_PACKAGE_ARCHITECTURE OUTPUT_STRIP_TRAILING_WHITESPACE ) set( CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME_LOWERCASE}-dev_${PROJECT_VERSION}-${PROJECT_VERSION_REVISION}_${CPACK_DEBIAN_PACKAGE_ARCHITECTURE}") else() set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME_LOWERCASE}_${PROJECT_VERSION}-${PROJECT_VERSION_REVISION}_${CMAKE_SYSTEM_NAME}") endif() # debian specific set(CPACK_DEBIAN_PACKAGE_NAME "casacore-dev") set(CPACK_DEBIAN_PACKAGE_SECTION "science") set(CPACK_DEBIAN_PACKAGE_PRIORITY "extra") set(CPACK_DEBIAN_PACKAGE_DEPENDS "libcfitsio3, libgfortran3") include(CPack) enable_language (Fortran) set (CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") # By default build only Python2 bindings option (BUILD_PYTHON "Build the python bindings" YES) option (BUILD_PYTHON3 "Build the python3 bindings" NO) # By default build shared libraries option (ENABLE_SHARED "Build shared libraries" YES) option (ENABLE_RPATH "Include rpath in executables and shared libraries" YES) # By default do not use HDF5, FFTW3, threads option (CXX11 "Compile as C++11 if possible" YES) option (ENABLE_TABLELOCKING "Make locking for concurrent table access possible" YES) option (USE_HDF5 "Build HDF5 " NO) option (USE_FFTW3 "Use FFTW instead of FFTPack" NO) option (USE_THREADS "Use Mutex thread synchronization" NO) option (USE_OPENMP "Use OpenMP threading" 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_FFTW3 ON) set(USE_OPENMP ON) set(USE_THREADS ON) set(UseCasacoreNamespace 1) # Force casacore routines to live in casacore rather than casa namespace ### ### after CASA switches from ASAP to libsakura, CASA will no longer ### use casacore's python module and it will no longer use boost... ### set(BUILD_PYTHON ON) 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 ) set(CXX11 ON) 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 (UseCasacoreNamespace) add_definitions (-DUseCasacoreNamespace) message (STATUS "Using namespace casacore.") else () message (STATUS "Namespace casacore 'merged' into namespace casa.") 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(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsigned-char") # 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) 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) # Detect if the compiler supports C++11 if we want to use it. if (CXX11) check_cxx_compiler_flag(-std=c++11 HAS_CXX11) if (HAS_CXX11) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") else() if (CASA_BUILD) message(FATAL_ERROR "CASA_BUILD requires a c++11 compatible compiler") else() message(FATAL_ERROR "C++11 build was requested but the compiler does not support -std=c++11") endif() endif() endif() # 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() else() # Ensure clang is not complaining about unused arguments. set(CMAKE_CXX_FLAGS "-Qunused-arguments ${CMAKE_CXX_FLAGS}") set(CMAKE_C_FLAGS "-Qunused-arguments ${CMAKE_C_FLAGS}") endif() # Since 2015, we need pwrite 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)) # 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 YES) set (_uselapack YES) 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) 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) find_package (DL) find_package (Readline) find_package (SOFA) 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 (USE_FFTW3) if (FFTW3_DISABLE_THREADS) find_package (FFTW3 COMPONENTS single double REQUIRED) else() find_package (FFTW3 COMPONENTS single double threads) endif (FFTW3_DISABLE_THREADS) endif (USE_FFTW3) 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 (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 (HDF5_FOUND) include_directories (${HDF5_INCLUDE_DIRS}) add_definitions(-DHAVE_HDF5) endif (HDF5_FOUND) if (FFTW3_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) endif (FFTW3_FOUND) 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_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(USE_OPENMP) # 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}" STREQUAL "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}" STREQUAL "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}" STREQUAL "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}" STREQUAL "Clang") endif(CMAKE_COMPILER_IS_GNUCXX) 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 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_VERSION}") set (LIB_SOVERSION "${PROJECT_VERSION_MAJOR}") 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) endforeach (module) # 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_OPENMP ............ = ${USE_OPENMP}") message (STATUS "USE_STACKTRACE ........ = ${USE_STACKTRACE}") message (STATUS "CMAKE_CXX_COMPILER .... = ${CMAKE_CXX_COMPILER}") message (STATUS "CMAKE_CXX_FLAGS ....... = ${CMAKE_CXX_FLAGS}") message (STATUS "C++11 support ......... = ${CXX11}") 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 "HDF5 library? ......... = ${HDF5_hdf5_LIBRARY}") message (STATUS "FFTW3 library? ........ = ${FFTW3_LIBRARIES}") 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 NO # ENABLE_SHARED YES # ENABLE_RPATH YES # CXX11 YES # ENABLE_TABLELOCKING YES # USE_HDF5 NO # USE_FFTW3 NO # USE_THREADS NO # USE_OPENMP 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 # LAPACK scimath # BLAS scimath # FFTW scimath (optional) # BOOST python (Boost-Python only) # PYTHON python # NUMPY python casacore-2.4.1/CONTRIB.md000066400000000000000000000045501321422335000147170ustar00rootroot00000000000000# 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 a procedure for contribution, 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 suit still runs. If it fails, fix the code or the relevant test * If you change a function/method fingerprint, update the documentation accordingly * If you add a function or method, add a test for it add it to the 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 (peer review) 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. * 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. * If you rewrite your history of your branch you can force push those changes to your branch. the PR will be updated. * We like to keep the history clean, and prevent a lot of 'merge branch x' or 'update README' commit messages. ## 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/ casacore-2.4.1/COPYING000066400000000000000000000430761321422335000143360ustar00rootroot00000000000000 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-2.4.1/README.md000066400000000000000000000071021321422335000145500ustar00rootroot00000000000000 # Casacore A suite of c++ libraries for radio astronomy data processing. # Installation ## Obtaining the source 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 (optional) * 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 libcfitsio3-dev \ wcslib-dev ``` and the optional libraries: ``` $ sudo apt-get install libhdf5-serial-dev libfftw3-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 -DUSE_FFTW3=ON -DDATA_DIR=/usr/share/casacore/data -DUSE_OPENMP=ON \ -DUSE_HDF5=ON -DBUILD_PYTHON=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. We have expirmental support for Python3 now. You can build python3 support using `-DBUILD_PYTHON3=on`. Note that CMake may have problems detecting the correct python3 libraries and headers, so probably you 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 Python2 specific settings use: ``` PYTHON2_EXECUTABLE PYTHON2_LIBRARY PYTHON2_INCLUDE_DIR ``` 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 will be necessary if you have the libraries from NRAO casa in your PATH or LD_LIBRARY_PATH. ## Ubuntu packages Casacore is part of the [kern suite](http://kernsuite.info), which supplies precompiled binaries for Ubuntu 14.04 and 16.04 # 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/CONTRIB.md), please read that first. # travis [![Build Status](https://travis-ci.org/casacore/casacore.svg?branch=master)](https://travis-ci.org/casacore/casacore) casacore-2.4.1/build-tools/000077500000000000000000000000001321422335000155265ustar00rootroot00000000000000casacore-2.4.1/build-tools/CMakeLists.txt000066400000000000000000000002751321422335000202720ustar00rootroot00000000000000# # CASA build-tools # install (PROGRAMS casacore_assay casacore_floatcheck casacore_memcheck DESTINATION bin ) install (FILES casacore_memcheck.supp DESTINATION share/casacore ) casacore-2.4.1/build-tools/casacore_assay000077500000000000000000000151601321422335000204370ustar00rootroot00000000000000#!/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: aips2-request@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 # $Id: assay,v 19.11 2006/10/17 03:41:50 gvandiep Exp $ #============================================================================= # 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* # If running on a Cray cluster, we have to use qsub and yod. # If found, the result is a path, thus starts with a slash. QSUBP=`which qsub 2>&1 | sed -e 's%^[^/].*%%'` YODP=`which yod 2>&1 | sed -e 's%^[^/].*%%'` if [ "$QSUBP" = "" -o "$YODP" = "" ] then # No Cray. eval "$COMMAND" > ${1}_tmp.out STATUS=$? if [ $STATUS != 0 ] then if [ $STATUS = 3 ] then echo "UNTESTED: $*" else echo "FAIL (execution failure): $*" fi exit fi else # .run files are not supported yet on the Cray. if [ -f "$1.run" ] then echo "UNTESTED (no .run support): $*" exit 3 fi # Create a batch job which will use a single processor. # Copy the AIPSPATH definition. # Because the job does not wait, we do that by using a wait file. # Note that yod does wait. rm -f $1_tmp.wait cat > $1_tmp.qsub < $1_tmp.out touch $1_tmp.wait EOF # Submit the job and wait for it to be finished. # Do not wait longer than 10 minutes. qsub $1_tmp.qsub NRTIMES=0 while [ ! -f $1_tmp.wait ] do if test $NRTIMES -gt 600 then # Keep job file. mv $1_tmp_job $1_job echo "FAIL (execution killed): $*" exit 1 fi sleep 1 NRTIMES=`expr $NRTIMES + 1` done 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-2.4.1/build-tools/casacore_cov000077500000000000000000000042641321422335000201110ustar00rootroot00000000000000#!/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-2.4.1/build-tools/casacore_floatcheck000077500000000000000000000064511321422335000214250ustar00rootroot00000000000000#!/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: aips2-request@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 (' \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: print sys.stderr, 'run as: floatcheck.py file1 file2 [tolerance]' return 1 tol = 1e-5; if len(argv) > 3: tol = float(argv[3]) # Regular expression for an integer or float number. numre = re.compile ('(?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-2.4.1/build-tools/casacore_memcheck000077500000000000000000000024161321422335000210730ustar00rootroot00000000000000#!/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 # 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 --workaround-gcc296-bugs=yes --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 casacore-2.4.1/build-tools/casacore_memcheck.supp000066400000000000000000000025551321422335000220620ustar00rootroot00000000000000# This file contains the valgrind errors to be suppressed for 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 Memcheck:Leak fun:_Znam fun:_ZN4casa19EnvironmentVariable3setERKNS_6StringES3_ } # Casacore's IO sometimes uses uninitialized fill characters. { Casacore-write-not-fully-initialized-buffer-LargeFilebufIO Memcheck:Param write(buf) fun:__write_nocancel fun:_ZN4casa9FiledesIO5writeExPKv } { Casacore-write-not-fully-initialized-buffer-FilebufIO Memcheck:Param write(buf) fun:__write_nocancel fun:_ZN4casa9FilebufIO11writeBufferExPKcx } # 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 } casacore-2.4.1/build-tools/doxygen_pp000077500000000000000000000530021321422335000176300ustar00rootroot00000000000000#!/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 # # $Id$ # 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; $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 $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; $comment =~ s||

    Example

    \\anchor ${hdrnm}_example|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
        // 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, uInt 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, uInt 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 (uInt& i, uInt& j) { uInt t = i; i = j; j = t; } template inline int GenSortIndirect::isAscending (const T* data, Int i, Int 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-2.4.1/casa/Utilities/GenSort.tcc000066400000000000000000000642741321422335000202440ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef CASA_GENSORT_TCC #define CASA_GENSORT_TCC #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; } #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); uInt* inx = indexVector.data(); 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 uInt* sf = inx+st; // first element uInt* 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 <= Int(k)) st = i; if (i >= Int(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 uInt GenSortIndirect::insSortAsc (uInt* inx, const T* data, Int 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 uInt GenSortIndirect::insSortAscDup (uInt* inx, const T* data, Int nr) { Int j; uInt cur; for (Int 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 uInt GenSortIndirect::insSortAscNoDup (uInt* inx, const T* data, Int nr) { if (nr < 2) { return nr; // nothing to sort } Int j, k; uInt cur; Int n = 1; for (Int i=1; i0 && data[inx[j-1]] > data[cur]) { j--; } if (j <= 0 || !(data[inx[j-1]] == data[cur])) { // no equal key for (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 (uInt* inx, const T* data, Int nr) { // Use the heapsort algorithm described by Jon Bentley in // UNIX Review, August 1992. inx--; Int 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 (uInt* inx, Int low, Int up, const T* data) { uInt sav = inx[low]; Int c; Int 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-2.4.1/casa/Utilities/LinearSearch.h000066400000000000000000000127231321422335000206710ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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. // // // //
      • operator(Int) or operator[Int] needs to be defined. //
      • The index must be zero based. //
      • 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. //
      • Member function nelements() is needed when the shorthand is taken. // // //
      • The equal operator (==) need to be defined. // // // //
      • I suspect that an implementation is possible that only calls // operator() or [] once during each evaluation of the while loop. //
      • 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-2.4.1/casa/Utilities/LinearSearch.tcc000066400000000000000000000051621321422335000212120ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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-2.4.1/casa/Utilities/MUString.cc000066400000000000000000000255201321422335000201760ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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 { static Regex ex("[ \t]"); return (ptr >= len || testChar(ex)); } Bool MUString::tSkipBlank() { return ((ptr < len && testBlank()) ? (skipBlank(), True) : False); } Bool MUString::testSign() const { static Regex ex("[-+]"); return testChar(ex); } 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 { Regex ex(String("[") + downcase(String(ch)) + upcase(String(ch)) + String("]")); return testChar(ex); } 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[]) { String a; String b; Int i; a = upcase(in); // Exact fit? for (i=0; i= N_name) { Int ia, ib; 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-2.4.1/casa/Utilities/MUString.h000066400000000000000000000304451321422335000200420ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef CASA_MUSTRING_H #define CASA_MUSTRING_H //# Includes #include #include #include //# Forward Declarations #include namespace casacore { //# NAMESPACE CASACORE - BEGIN class Regex; template class Vector; // // Pointed String class to aid analysis of quantity strings // // // // //
      • 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. // // // //
      • 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-2.4.1/casa/Utilities/Notice.cc000066400000000000000000000054051321422335000177070ustar00rootroot00000000000000//# Notice.cc: Classes for mainpulating notices //# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include namespace casacore { //# NAMESPACE CASACORE - BEGIN Notice::~Notice() { // Nothing } NoticeSource::~NoticeSource(){ while (head()) head()->val()->unlink(); } void NoticeSource::notify(const Notice ¬e) { Link *t = 0; for (Link *tmp = curIters;tmp;tmp=t) { t = (*tmp).next(); tmp->val()->notify(note); } } NoticeTarget::~NoticeTarget() { unlink(); } void NoticeTarget::unlink() { if (ilink) { if (container && ilink == (*container).head()) (*container).head() = (*ilink).unlink(); else (*ilink).unlink(); delete ilink; container = 0; ilink = 0; } valid = False; } void NoticeTarget::attach(NoticeSource *v) { if ( v ) { unlink(); container = v; (*v).head() = ilink = new Link(this,0,(*v).head()); valid = True; } } void NoticeTarget::attach(NoticeSource &v) { unlink(); container = &v; v.head() = ilink = new Link(this,0,v.head()); valid = True; } void NoticeTarget::link(const NoticeTarget &other) { if (other.isValid()) { unlink(); container = other.container; (*container).head() = ilink = new Link(this,0,(*container).head()); valid = True; } } void NoticeTarget::link(const NoticeTarget *other) { if (other && (*other).isValid()) { unlink(); container = (*other).container; (*container).head() = ilink = new Link(this,0,(*container).head()); valid = True; } } } //# NAMESPACE CASACORE - END casacore-2.4.1/casa/Utilities/Notice.h000066400000000000000000000211351321422335000175470ustar00rootroot00000000000000//# Notice.h: Classes for manipulating notices //# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef CASA_NOTICE_H #define CASA_NOTICE_H #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declaration class NoticeTarget; // abstract base class for notices // // // // // A Notice is the piece of information passed around // between a NoticeSource and a NoticeTarget. This // abstract base class is only a skeleton intended to be derived from. It // does not contain any relevant information -- that must be added by // the derived classes --, but it enforces derived classes to implement // the comparison operator == and the function // type(). // // // ListNotice, derived from // Notice, is the notification which is passed between // List and // ListIter // to keep cursors and container in sync. // class Notice { public: Notice() {} virtual ~Notice(); // Return the identification number of the Notice type. virtual uInt type() const = 0; // Compare two Notices. virtual int operator==(const Notice &) const = 0; }; // base class for notice originators // // // // // A NoticeSource maintains a list of all of the // NoticeTargets which are interested in Notices // from this NoticeSource. Its member function // notify() sends the specified Notice to all the // NoticeTargets in the list. // // Classes which have many other dependent objects which need to be // updated, should derive from this class. // // // List, the linked list class, is // derived from NoticeSource. It mainly contains status // information; all the manipulation functions are located in the // ListIter classes. The linked // list and its iterators communicate with each other via the notice // system. List does not provide any further notice // functionality; everything is taken care of by its base class // NoticeSource. // class NoticeSource { public: friend class NoticeTarget; NoticeSource() : curIters(0) {} virtual ~NoticeSource(); // Sends the note to all NoticeTargets in the // target list. void notify(const Notice & note); private: Link *curIters; //# Do not Delete Link *&head() { return curIters;} }; // abstract base class for notice receptors // // // // // A NoticeTarget receives the Notices from the // NoticeSource to which it is linked. A target can only be // linked to one source. // // Classes which are dependent upon a particular // NoticeSource should derive from this class. // // // ListIter and its parent class // ConstListIter are the iterators or // "dynamic" cursors in the linked List. They // are derived from NoticeTarget, and the notice system ensures // that multiple cursors are updated as elements are added and removed from // the list, according to the following scheme: //
          //
        1. An iterator changes something to the underlying list. //
        2. The iterator creates a ListNotice // containing all the necessary information about the change. //
        3. The iterator passes the notice to the NoticeSource // List. //
        4. The list relays the notice to all other iterators operating on the // list (kept in the "target list"). //
        5. Every iterator catches the notice and changes its state accordingly. //
        //
        class NoticeTarget { public: friend class NoticeSource; // Destructs this NoticeTarget. virtual ~NoticeTarget(); // Returns a boolean value telling whether this NoticeTarget // is still "valid". Bool isValid() const {return valid;} // Returns a boolean value telling whether this NoticeTarget // is still attached to a NoticeSource or not. Bool isAttached() const {return ilink ? True : False;} // Makes the current NoticeTarget "invalid". void invalidate() {valid = False;} // Hook through which NoticeTargets are notified // (by NoticeSources). virtual void notify(const Notice &) = 0; protected: Link *ilink; NoticeSource *container; Bool valid; // Creates an unlinked, "invalid" NoticeTarget. An invalid // NoticeTarget does not occur in the target list of any // NoticeSource. NoticeTarget() : ilink(0), container(0), valid(False) {} // Creates a "valid" NoticeTarget linked to the specified // NoticeSource. The NoticeTarget will be added // to the target list in that NoticeSource. // NoticeTarget(NoticeSource *v) : ilink(0), container(0), valid(False) {attach(v);} NoticeTarget(NoticeSource &v) : ilink(0),container(0), valid(False) {attach(v);} // // Creates a "valid" NoticeTarget linked to the same // NoticeSource as the other NoticeTarget. // So, both NoticeTargets will occur in the same target // list. // NoticeTarget(NoticeTarget &other) : ilink(0), container(0), valid(False) { if (other.isValid()) attach(other.container);} NoticeTarget(NoticeTarget *other) : ilink(0), container(0), valid(False) { if (other && other->isValid()) attach( (*other).container );} // // Unlinks this NoticeTarget from its NoticeSource. // The NoticeTarget will be removed from the target list. void unlink(); // Links this NoticeTarget to the same NoticeSource // as the other NoticeTarget. Any previous link will be undone. // void link(const NoticeTarget &other); void link(const NoticeTarget *other); // // Retrieves the next NoticeTarget in the target list // of the associated NoticeSource. // Link *next() { return(ilink ? (*ilink).next() : 0); } const Link *next() const { return(ilink ? (*ilink).next() : 0); } // // Adds this NoticeTarget to the target list in the // specified NoticeSource, so that it will receive all // notices sent out by that NoticeSource. // void attach(NoticeSource *v); void attach(NoticeSource &v); // }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/casa/Utilities/Precision.cc000066400000000000000000000056301321422335000204210ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/casa/Utilities/Precision.h000066400000000000000000000036201321422335000202600ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/casa/Utilities/PtrHolder.h000066400000000000000000000210161321422335000202270ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef CASA_PTRHOLDER_H #define CASA_PTRHOLDER_H //# Includes #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Hold and delete pointers not deleted by object destructors // // // // // //
      • 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. // // //
      • Use the autoptr class from the Standard Library // template class PtrHolder { public: // The default constructor uses a null pointer. 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. 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 // // // // // //
      • 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-2.4.1/casa/Utilities/PtrHolder.tcc000066400000000000000000000044031321422335000205520ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/casa/Utilities/RecordTransformable.cc000066400000000000000000000034751321422335000224310ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/casa/Utilities/RecordTransformable.h000066400000000000000000000131271321422335000222660ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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 // // // // //
      • 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. // // // //
      • 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-2.4.1/casa/Utilities/RegSequence.cc000066400000000000000000000030061321422335000206670ustar00rootroot00000000000000//# RegSequence.cc: Sequence class for the Register template functions //# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include namespace casacore { //# NAMESPACE CASACORE - BEGIN uInt RegSequence::num = 0; Mutex RegSequence::theirMutex; uInt RegSequence::getNext() { ScopedMutexLock lock(theirMutex); return ++num; } } //# NAMESPACE CASACORE - END casacore-2.4.1/casa/Utilities/RegSequence.h000066400000000000000000000047031321422335000205360ustar00rootroot00000000000000//# RegSequence.h: Sequence for the Register template functions //# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef CASA_REGSEQUENCE_H #define CASA_REGSEQUENCE_H #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // Sequence for the Register() template functions // // // // //
      • class Sequence //
      • global function Register // // // This class provides a Sequence for the // Register() template // functions. Providing a separate sequence exclusively for // Register() makes it less likely that the uInt // counter will overflow. // class RegSequence : public Sequence { public: // Get the next uInt value in the sequence (thread-safe). // uInt getNext(); static uInt SgetNext() {return ++num;} // private: static uInt num; static Mutex theirMutex; }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/casa/Utilities/Regex.cc000066400000000000000000000337421321422335000175450ustar00rootroot00000000000000//# Regex.cc: Regular expression class //# Copyright (C) 1993,1994,1995,1996,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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ // Regex class implementation #include #include #include #include #include #include #include //# for memcpy with gcc-4.3 #include namespace casacore { //# NAMESPACE CASACORE - BEGIN Regex::Regex() { create("",0,0,0); } void Regex::create(const String& exp, Int fast, Int bufsize, const Char* transtable) { str = exp; fastval = fast; bufsz = bufsize; trans = 0; if (transtable) { trans = new Char[256]; memcpy(trans, transtable, 256); } Int tlen = exp.length(); buf = new re_pattern_buffer; reg = new re_registers; if (fast) buf->fastmap = new Char[256]; else buf->fastmap = 0; buf->translate = trans; if (tlen > bufsize) bufsize = tlen; buf->allocated = bufsize; buf->buffer = (Char *) malloc(buf->allocated); Int orig = a2_re_set_syntax(RE_NO_BK_PARENS+ // use () for grouping RE_NO_BK_VBAR+ // use | for OR RE_INTERVALS+ // intervals are possible RE_NO_BK_CURLY_BRACES+ // use {} for interval RE_CHAR_CLASSES+ // [:upper:], etc. possible RE_NO_EMPTY_BK_REF+ // backreferences possible RE_NO_EMPTY_RANGES+ // e.g. [z-a] is empty set RE_CONTEXTUAL_INVALID_OPS); const char* msg = a2_re_compile_pattern((Char*)(exp.chars()), tlen, buf); a2_re_set_syntax(orig); if (msg != 0) { throw(invalid_argument("Regex: invalid regular expression " + exp + " given (" + String(msg) + ')')); } else if (fast) a2_re_compile_fastmap(buf); } void Regex::dealloc() { if ( buf != 0 ) { free(buf->buffer); delete [] buf->fastmap; delete buf; buf= 0; } delete reg; reg=0; delete [] trans; trans=0; } Int Regex::match_info(Int& start, Int& length, Int nth) const { if ((unsigned)(nth) >= RE_NREGS) return 0; else { start = reg->start[nth]; length = reg->end[nth] - start; return start >= 0 && length >= 0; } } Bool Regex::OK() const { Bool v = buf != 0; // have a regex buf v &= buf->buffer != 0; // with a pat return v; } ostream &operator<<(ostream &ios, const Regex &exp) { return ios << exp.str; } 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); } String::size_type Regex::search(const Char *s, String::size_type len, Int &matchlen, Int pos) const { Int matchpos, xpos, range; if (pos >= 0) { xpos = pos; range = len - pos; } else { xpos = len + pos; range = -xpos; } matchpos = a2_re_search_2(buf, 0, 0, (Char*)s, len, xpos, range, reg, len); if (matchpos >= 0) matchlen = reg->end[0] - reg->start[0]; else { matchlen = 0; return String::npos; } return matchpos; } String::size_type Regex::match(const Char *s, String::size_type len, String::size_type pos) const { Int res; Int ps = static_cast(pos); if (ps < 0) { ps += len; if (ps > static_cast(len)) return String::npos; res = a2_re_match_2(buf, 0, 0, (Char*)s, ps, 0, reg, ps); } else if (ps > static_cast(len)) return String::npos; else res = a2_re_match_2(buf, 0, 0, (Char*)s, len, ps, reg, len); if (res < 0) return String::npos; return static_cast(res); } Regex::Regex(const String &exp, Bool fast, Int sz, const Char *translation) { create(exp, fast, sz, translation); } Regex::~Regex() { dealloc(); } Regex::Regex(const Regex &that) : RegexBase() { create(that.str, that.fastval, that.bufsz, that.trans); } Regex &Regex::operator=(const Regex &that) { dealloc(); create(that.str, that.fastval, that.bufsz, that.trans); return *this; } Regex &Regex::operator=(const String &strng) { dealloc(); create(strng, 0, 40, 0); return *this; } String Regex::fromPattern(const String &pattern) { enum CState{stream, bracketopen, escapechar}; uInt bracecount = 0; Int inbrcount = -1; Bool skipChar = False; Bool charClass = False; vector 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 && strng[i+1] == '-' && isalpha(strng[i+2])) { i += 2; int ec = strng[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]+", 1); const Regex RXint("-?[0-9]+", 1); const Regex RXdouble("-?(([0-9]+\\.[0-9]*)|([0-9]+)|(\\.[0-9]+))([eE][+-]?[0-9]+)?", 1, 200); const Regex RXalpha("[A-Za-z]+", 1); const Regex RXlowercase("[a-z]+", 1); const Regex RXuppercase("[A-Z]+", 1); const Regex RXalphanum("[0-9A-Za-z]+", 1); const Regex RXidentifier("[A-Za-z_][A-Za-z0-9_]*", 1); } //# NAMESPACE CASACORE - END casacore-2.4.1/casa/Utilities/Regex.h000066400000000000000000000322561321422335000174060ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef CASA_REGEX_H #define CASA_REGEX_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward declarations. struct re_pattern_buffer; struct re_registers; // // Regular expression class // // // // // // This class provides regular expression functionality, such as // matching and searching in strings, comparison of expressions, and // input/output. It is built on the regular expression functions in the // GNU library (see files cregex.h and cregex.cc). //
        Apart from proper regular expressions, it also supports glob patterns // (UNIX file name patterns) by means of a conversion to a proper regex. // Also ordinary strings can be converted to a proper regex. //

        // cregex.cc supports many syntaxes. Regex supports // only one syntax, the extended regular expression with { and not \\{ // as a special character. The special characters are: //

        //
        ^ //
        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]+", 1); // (blank, newline, tab, return, vertical tab, formfeed) // Regex RXint("-?[0-9]+", 1); // Regex RXdouble("-?(([0-9]+\\.[0-9]*)|([0-9]+)|(\\.[0-9]+))([eE][+-]?[0-9]+)?", 1, 200); // Regex RXalpha("[A-Za-z]+", 1); // Regex RXlowercase("[a-z]+", 1); // Regex RXuppercase("[A-Z]+", 1); // Regex RXalphanum("[0-9A-Za-z]+", 1); // Regex RXidentifier("[A-Za-z_][A-Za-z0-9_]*", 1); // // 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" // // // //
      • Let sgi ifdef go //
      • Decide on documentation of GNU stuff (cregex.h, cregex.cc) // class Regex : public RegexBase { public: // Default constructor uses a zero-length regular expression. // //
      • invalid_argument // Regex(); // Construct a regular expression. // Optionally a fast map can be created, a buffer size can be given // and a translation table (of 256 chars) can be applied. // The translation table can, for instance, be used to map // lowercase characters to uppercase. // See cregex.cc (the extended regular expression matching and search // library) for detailed information. // //
      • invalid_argument // Regex(const String &exp, Bool fast = False, Int sz = 40, const Char *translation = 0); // Copy constructor (copy semantics). // //
      • invalid_argument // Regex(const Regex &that); virtual ~Regex(); // Assignment (copy semantics). // //
      • invalid_argument // // Regex &operator=(const Regex &that); Regex &operator=(const String &strng); // // Convert a shell-like pattern to a regular expression. // 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. // This is useful TaQL which mimics SQL. static String fromSQLPattern(const String &pattern); // Convert a normal string to a regular expression. // 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 &strng); // Create a case-insensitive reular 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 &strng); // Get the regular expression string. const String ®exp() const { return str; } // Get the translation table (can be a zero pointer). const Char *transtable() const { return trans; } // Test if the regular expression matches (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 match at end. // // // Use the appropriate String functions // to test if a string matches a regular expression. // Regex::match is pretty low-level. // virtual String::size_type match(const Char *s, String::size_type len, String::size_type pos=0) const; // Test if the regular expression occurs 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. // The search will do a reverse search if the pos given is less than 0. // // Use the appropriate String functions // to test if a regular expression occurs in a string. // Regex::search is pretty low-level. // // virtual String::size_type search(const Char *s, String::size_type len, Int &matchlen, Int pos=0) const; virtual String::size_type find(const Char *s, String::size_type len, Int &matchlen, String::size_type pos=0) const; // // Return some internal cregex info. Int match_info(Int& start, Int& length, Int nth = 0) const; // Does it contain a valid Regex? Bool OK() const; // Write as ASCII. friend ostream &operator<<(ostream &ios, const Regex &exp); protected: String str; // the reg. exp. string Int fastval; // fast flag Int bufsz; // buffer size given Char* trans; // possible translation table re_pattern_buffer* buf; // compiled reg.exp. re_registers* reg; // internal reg.exp. stuff // Compile the regular expression // //
      • invalid_argument // void create(const String&, Int, Int, const Char*); // Deallocate the stuff allocated by create. void dealloc(); }; // 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-2.4.1/casa/Utilities/Register.h000066400000000000000000000117351321422335000201170ustar00rootroot00000000000000//# Register.h: Templated function to provide simple type identification //# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef CASA_REGISTER_H #define CASA_REGISTER_H #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Primitive Run Time Type Information (RTTI) // // // // // // //
      • none // // // // This function is called Register because the user is registering // a type with the run-time type mechanism. // // // // This function returns a unique unsigned integer (uInt) for each // type of pointer the user passes in as a parameter. It will always return // the same number for a given type during a particular execution. The // unsigned integer which is returned can be use to identify the particular // type. // // This function does not work correctly for // multiple inheritance, but ONLY for // single inheritance. In addition, the type number // which is returned is NOT unique across program executions. // // // This RTTI mechanism is simple, it does not require extra functions to be // added to the classes which are to be identified, and it is // similar to the RTTI mechanism which will be a part of the C++ language // in the future. // // To be useful, however, this mechanism must be used as part of the // implementation of a virtual member function. For example: // // #include // #include // // class foo { public: virtual uInt type() { return Register(this);}}; // class bar : public foo { public: uInt type() { return Register(this);}}; // main() { // foo *b = new bar(); // foo *f = new foo(); // // cout << "f: type()=" << f->type() << " Register()=" << Register(f) << endl; // cout << "b: type()=" << b->type() << " Register()=" << Register(b) << endl; // } // // The output of this code would look something like: //
        //        f: type()=1 Register()=1
        //        b: type()=2 Register()=1
        //    
        // Without the virtual function, type(), the output of // Register() is deceiving and of little use. //
        // // // Needed a simple type identification mechanism for the // Notice class. This was necessary so that // multiple notices could be distinguished. // It can be replaced by the future standard RTTI. // // // //
      • none // // // // This is a templated function which takes a pointer to any class as a parameter. // The parameter's type is then used to generate a unique id. The parameter is // a pointer rather than a value for efficiency reasons. With a // value parameter, it would be difficult to do things like: // // Register(static_cast(0)); // // to find the Register type id for a random class. template uInt Register(const t *); // // Bother!! // template inline uInt Register(const t *) { // static uInt type = 0; // if (!type) type = RegSequence::SgetNext(); // return type; // } // BOTHER!! BOTHER!! BOTHER!! // template inline uInt Register(const t &v) { // return Register(&v); // } } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-2.4.1/casa/Utilities/Register.tcc000066400000000000000000000030741321422335000204360ustar00rootroot00000000000000//# Register.cc: Templated function to provide simple type identification //# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef CASA_REGISTER_TCC #define CASA_REGISTER_TCC #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template uInt Register(const t *) { static uInt type = 0; if (!type) type = RegSequence::SgetNext(); return type; } } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/casa/Utilities/Sequence.h000066400000000000000000000052311321422335000200750ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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: static uInt num; static Mutex theirMutex; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-2.4.1/casa/Utilities/Sequence.tcc000066400000000000000000000030371321422335000204210ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/casa/Utilities/Sequence2.cc000066400000000000000000000027741321422335000203260ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include namespace casacore { //# NAMESPACE CASACORE - BEGIN uInt uIntSequence::num = 0; Mutex uIntSequence::theirMutex; uInt uIntSequence::SgetNext() { ScopedMutexLock lock(theirMutex); return ++num; } } //# NAMESPACE CASACORE - END casacore-2.4.1/casa/Utilities/Sort.cc000066400000000000000000000402531321422335000174150ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include #include #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 CountedPtr& 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; } 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 (uInt 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 CountedPtr& 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::unique (Vector& uniqueVector, uInt nrrec) const { Vector indexVector(nrrec); indgen (indexVector); return unique (uniqueVector, indexVector); } uInt Sort::unique (Vector& uniqueVector, const Vector& indexVector) const { uInt nrrec = indexVector.nelements(); uniqueVector.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; const uInt* inx = indexVector.getStorage (delInx); uInt* uniq = uniqueVector.getStorage (delUniq); uniq[0] = 0; uInt nruniq = 1; for (uInt i=1; i& indexVector, uInt 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; uInt* 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); } uInt 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; } uInt Sort::parSort (int nthr, uInt nrrec, uInt* 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=1; j--) { siftDown (j, nrrec, inx); } for (j=nrrec; j>=2; j--) { swap (1, j, inx); siftDown (1, j-1, inx); } return nrrec; } uInt Sort::heapSortNoDup (uInt nrrec, uInt* inx) const { heapSort (nrrec, inx); return insSortNoDup (nrrec, inx); } void Sort::siftDown (Int low, Int up, uInt* inx) const { uInt sav = inx[low]; Int c; Int 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 int Sort::compare (uInt i1, uInt i2) const { int seq; SortKey* skp; for (uInt 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) return 2; // in order if (seq != 0) { return 0; // out-of-order } } // Equal keys, so return i1 #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations template class Vector; // 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 CountedPtr 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 CountedPtr&, 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; // 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 CountedPtr for memory management CountedPtr 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 CountedPtr 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 CountedPtr&, uInt increment, Order = Ascending); void sortKey (uInt offset, DataType, Order = Ascending); void sortKey (uInt offset, const CountedPtr&, 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; // 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. // uInt unique (Vector& uniqueVector, uInt nrrec) const; uInt unique (Vector& uniqueVector, const Vector& indexVector) const; // private: // Copy that Sort object to this. void copy (const Sort& that); // Add a sort key giving a data type or the sort key. // void addKey (const void* data, DataType, uInt nr, int options); void addKey (SortKey*); // // Do an insertion sort, optionally skipping duplicates. // uInt insSort (uInt nr, uInt* indices) const; uInt insSortNoDup (uInt nr, uInt* 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. uInt parSort (int nthr, uInt nrrec, uInt* inx) const; void merge (uInt* inx, uInt* tmp, uInt size, uInt* index, uInt nparts) const; // Do a quicksort, optionally skipping duplicates // (qkSort is the actual quicksort function). // uInt quickSort (uInt nr, uInt* indices) const; uInt quickSortNoDup (uInt nr, uInt* indices) const; void qkSort (Int nr, uInt* indices) const; // // Do a heapsort, optionally skipping duplicates. // uInt heapSort (uInt nr, uInt* indices) const; uInt heapSortNoDup (uInt nr, uInt* indices) const; // // Siftdown algorithm for heapsort. void siftDown (Int low, Int up, uInt* indices) const; // Compare the keys of 2 records. int compare (uInt index1, uInt index2) const; // Swap 2 indices. inline void swap (Int index1, Int index2, uInt* indices) const; PtrBlock keys_p; //# keys to sort on uInt 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 }; inline void Sort::swap (Int i, Int j, uInt* inx) const { uInt t = inx[i]; inx[i] = inx[j]; inx[j] = t; } } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/casa/Utilities/SortError.cc000066400000000000000000000041561321422335000204310ustar00rootroot00000000000000//# SortError.cc: Error classes for the sort class //# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include 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 () throw() { ; } SortInvDT::SortInvDT (Category c) : SortError ("Invalid sort data type",c) { ; } SortInvDT::~SortInvDT () throw() { ; } SortInvIncr::SortInvIncr (Category c) : SortError ("Sort increment < key Incr",c) { ; } SortInvIncr::~SortInvIncr () throw() { ; } SortNoData::SortNoData (Category c) : SortError ("No data array given to constructor",c) { ; } SortNoData::~SortNoData () throw() { ; } SortInvOpt::SortInvOpt (Category c) : SortError ("Invalid sort option given",c) { ; } SortInvOpt::~SortInvOpt () throw() { ; } } //# NAMESPACE CASACORE - END casacore-2.4.1/casa/Utilities/SortError.h000066400000000000000000000075201321422335000202710ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef CASA_SORTERROR_H #define CASA_SORTERROR_H #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // Generic Sort exception // // // //
      • 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 () throw(); }; // Invalid data type used for this sort key // // // //
      • Sort // // // Invalid data type used for this sort key // class SortInvDT : public SortError { public: SortInvDT (Category c=INVALID_ARGUMENT); ~SortInvDT () throw(); }; // Invalid increment used for this sort key // // // //
      • 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 () throw(); }; // No data array given to Sort constructor. // // // //
      • Sort // // // No data array has been given to Sort constructor. // class SortNoData : public SortError { public: SortNoData (Category c=INITIALIZATION); ~SortNoData () throw(); }; // Invalid sort option given to routine dosort. // // // //
      • Sort // // // Invalid sort option has been given to routine dosort. // class SortInvOpt : public SortError { public: SortInvOpt (Category c=INVALID_ARGUMENT); ~SortInvOpt () throw(); }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/casa/Utilities/StringDistance.cc000066400000000000000000000134501321422335000214060ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/casa/Utilities/Template.h000066400000000000000000000216151321422335000201040ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 // // // // // //
      • 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. // // // //
      • 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-2.4.1/casa/Utilities/Template.tcc000066400000000000000000000625121321422335000204270ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/casa/Utilities/ValType.cc000066400000000000000000000362201321422335000200510ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 = -C::minfloat; const Complex ValType::undefcomplex (-C::minfloat, -C::minfloat); const double ValType::undefdouble = -C::mindouble; const DComplex ValType::undefdcomplex (-C::mindouble, -C::mindouble); const String ValType::undefstring (""); const String ValType::strbool = "Bool "; const String ValType::strchar = "Char "; const String ValType::struchar = "uChar "; const String ValType::strshort = "Short "; const String ValType::strushort = "uShort "; const String ValType::strint = "Int "; const String ValType::struint = "uInt "; const String ValType::strint64 = "Int64 "; const String ValType::strfloat = "float "; const String ValType::strdouble = "double "; const String ValType::strcomplex = "Complex "; const String ValType::strdcomplex = "DComplex"; const String ValType::strstring = "String "; const String ValType::strrecord = "Record "; const String ValType::strtable = "Table "; const String ValType::strother = "Other "; const String ValType::strunknown = "unknown "; //# 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; case TpFloat: case TpArrayFloat: readFunc = CanonicalConversion::getToLocal (static_cast(0)); writeFunc = CanonicalConversion::getFromLocal (static_cast(0)); break; case TpDComplex: case TpArrayDComplex: nrElementsPerValue = 2; 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; case TpFloat: case TpArrayFloat: readFunc = LECanonicalConversion::getToLocal (static_cast(0)); writeFunc = LECanonicalConversion::getFromLocal (static_cast(0)); break; case TpDComplex: case TpArrayDComplex: nrElementsPerValue = 2; 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; case TpShort: if (to == TpInt) return True; case TpInt: if (to == TpInt64) return True; case TpInt64: case TpFloat: case TpDouble: if (to == TpFloat || to == TpDouble) return True; case TpComplex: case TpDComplex: if (to == TpComplex || to == TpDComplex) return True; return False; case TpUChar: if (to == TpUShort) return True; case TpUShort: if (to == TpUInt) return True; 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. CountedPtr ValType::getCmpObj (DataType dt) { switch (dt) { case TpBool: return new ObjCompare(); case TpChar: return new ObjCompare(); case TpUChar: return new ObjCompare(); case TpShort: return new ObjCompare(); case TpUShort: return new ObjCompare(); case TpInt: return new ObjCompare(); case TpUInt: return new ObjCompare(); case TpInt64: return new ObjCompare(); case TpFloat: return new ObjCompare(); case TpDouble: return new ObjCompare(); case TpComplex: return new ObjCompare(); case TpDComplex: return new ObjCompare(); case TpString: return new ObjCompare(); default: break; } return CountedPtr(); } } //# NAMESPACE CASACORE - END casacore-2.4.1/casa/Utilities/ValType.h000066400000000000000000000502251321422335000177140ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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. // // // // // //
      • 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 CountedPtr 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 const String strchar; static const String struchar; static const String strshort; static const String strushort; static const String strint; static const String struint; static const String strint64; static const String strfloat; static const String strdouble; static const String strcomplex; static const String strdcomplex; static const String strstring; static const String strrecord; static const String strtable; static const String strother; static const String strunknown; // // 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-2.4.1/casa/Utilities/ValTypeId.h000066400000000000000000000063641321422335000201760ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef CASA_VALTYPEID_H #define CASA_VALTYPEID_H #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // The id-string of a value type // // // // // //
      • 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 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-2.4.1/casa/Utilities/cregex.cc000066400000000000000000002342501321422335000177450ustar00rootroot00000000000000/* cregex.c: Extended regular expression matching and search library Copyright (C) 1993,1994,1995,1996,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: aips2-request@nrao.edu. Postal address: AIPS++ Project Office National Radio Astronomy Observatory 520 Edgemont Road Charlottesville, VA 22903-2475 USA */ //# $Id$ /* Get the interface, including the syntax bits. */ #include #include #include /* We write fatal error messages on standard error. */ #include /* isalpha(3) etc. are used for the character classes. */ /* The CB_CTYPE_MACROS causes incompatible ctype definitions on */ /* HPUX 10.2 systems, so we undefine it AxC 23-1-2003 */ #if defined (HPUX10_2) # undef _SB_CTYPE_MACROS #endif #include /* Sequents are missing isgraph. */ /* Get the RE_DUP_MAX value */ #include // The Intel compiler buggers up limits.h so RE_DUP_MAX doesn't get defined so // if it's not defined we will do it here via the regex.h include. #ifndef RE_DUP_MAX #include #endif namespace casacore { //# NAMESPACE CASACORE - BEGIN // Define some global variables; before they were externs in the .h file. /* Set by a2_re_set_syntax to the current regexp syntax to recognize. */ int obscure_syntax = 0; char re_syntax_table[256]; /* Roughly the maximum number of failure points on the stack. Would be exactly that if always pushed MAX_NUM_FAILURE_ITEMS each time we failed. */ int re_max_failures = 2000; // Hack to avoid requiring alloca. Define an instance of this at the beginning // of your function and call it alloca. class cregex_allocator { public: cregex_allocator(); ~cregex_allocator(); char * operator()(int nbytes); private: enum {max_allocations_p = 128}; char *allocated_table_p[max_allocations_p]; int pos_p; cregex_allocator(const cregex_allocator &); cregex_allocator& operator=(const cregex_allocator &); }; cregex_allocator::cregex_allocator() : pos_p(-1) { } cregex_allocator::~cregex_allocator() { for (int i=0; i <= pos_p; i++) { delete [] allocated_table_p[i]; } } char *cregex_allocator::operator()(int nbytes) { if (pos_p >= max_allocations_p-1) { fprintf(stderr, "cregex.cc: larger allocation table needed\n"); return 0; } pos_p++; return (allocated_table_p[pos_p] = new char[nbytes]); } /* Define the syntax stuff, so we can do the \<, \>, etc. */ /* This must be nonzero for the wordchar and notwordchar pattern commands in a2_re_match_2. */ #ifndef Sword #define Sword 1 #endif #define SYNTAX(c) re_syntax_table[c] static void init_syntax_once () { int c; static int done = 0; if (done) return; memset (re_syntax_table, 0, sizeof re_syntax_table); for (c = 'a'; c <= 'z'; c++) re_syntax_table[c] = Sword; for (c = 'A'; c <= 'Z'; c++) re_syntax_table[c] = Sword; for (c = '0'; c <= '9'; c++) re_syntax_table[c] = Sword; done = 1; } #ifndef isgraph #define isgraph(c) (isprint((c)) && !isspace((c))) #endif /* These are the command codes that appear in compiled regular expressions, one per byte. Some command codes are followed by argument bytes. A command code can specify any interpretation whatsoever for its arguments. Zero-bytes may appear in the compiled regular expression. The value of `exactn' is needed in search.c (search_buffer) in emacs. So regex.h defines a symbol `RE_EXACTN_VALUE' to be 1; the value of `exactn' we use here must also be 1. */ enum regexpcode { unused=0, exactn=1, /* Followed by one byte giving n, then by n literal bytes. */ begline, /* Fail unless at beginning of line. */ endline, /* Fail unless at end of line. */ jump, /* Followed by two bytes giving relative address to jump to. */ on_failure_jump, /* Followed by two bytes giving relative address of place to resume at in case of failure. */ finalize_jump, /* Throw away latest failure point and then jump to address. */ maybe_finalize_jump, /* Like jump but finalize if safe to do so. This is used to jump back to the beginning of a repeat. If the command that follows this jump is clearly incompatible with the one at the beginning of the repeat, such that we can be sure that there is no use backtracking out of repetitions already completed, then we finalize. */ dummy_failure_jump, /* Jump, and push a dummy failure point. This failure point will be thrown away if an attempt is made to use it for a failure. A + construct makes this before the first repeat. Also use it as an intermediary kind of jump when compiling an or construct. */ succeed_n, /* Used like on_failure_jump except has to succeed n times; then gets turned into an on_failure_jump. The relative address following it is useless until then. The address is followed by two bytes containing n. */ jump_n, /* Similar to jump, but jump n times only; also the relative address following is in turn followed by yet two more bytes containing n. */ set_number_at, /* Set the following relative location to the subsequent number. */ anychar, /* Matches any (more or less) one character. */ charset, /* Matches any one char belonging to specified set. First following byte is number of bitmap bytes. Then come bytes for a bitmap saying which chars are in. Bits in each byte are ordered low-bit-first. A character is in the set if its bit is 1. A character too large to have a bit in the map is automatically not in the set. */ charset_not, /* Same parameters as charset, but match any character that is not one of those specified. */ start_memory, /* Start remembering the text that is matched, for storing in a memory register. Followed by one byte containing the register number. Register numbers must be in the range 0 through RE_NREGS. */ stop_memory, /* Stop remembering the text that is matched and store it in a memory register. Followed by one byte containing the register number. Register numbers must be in the range 0 through RE_NREGS. */ duplicate, /* Match a duplicate of something remembered. Followed by one byte containing the index of the memory register. */ before_dot, /* Succeeds if before point. */ at_dot, /* Succeeds if at point. */ after_dot, /* Succeeds if after point. */ begbuf, /* Succeeds if at beginning of buffer. */ endbuf, /* Succeeds if at end of buffer. */ wordchar, /* Matches any word-constituent character. */ notwordchar, /* Matches any char that is not a word-constituent. */ wordbeg, /* Succeeds if at word beginning. */ wordend, /* Succeeds if at word end. */ wordbound, /* Succeeds if at a word boundary. */ notwordbound,/* Succeeds if not at a word boundary. */ syntaxspec, /* Matches any character whose syntax is specified. followed by a byte which contains a syntax code, e.g., Sword. */ notsyntaxspec /* Matches any character whose syntax differs from that specified. */ }; /* Number of failure points to allocate space for initially, when matching. If this number is exceeded, more space is allocated, so it is not a hard limit. */ #ifndef NFAILURES #define NFAILURES 80 #endif /* Store NUMBER in two contiguous bytes starting at DESTINATION. */ #define STORE_NUMBER(destination, number) \ { (destination)[0] = (signed char)((number) & 0377); \ (destination)[1] = (signed char)((number) >> 8); } /* Same as STORE_NUMBER, except increment the destination pointer to the byte after where the number is stored. Watch out that values for DESTINATION such as p + 1 won't work, whereas p will. */ #define STORE_NUMBER_AND_INCR(destination, number) \ { STORE_NUMBER(destination, number); \ (destination) += 2; } /* Put into DESTINATION a number stored in two contingous bytes starting at SOURCE. */ #define EXTRACT_NUMBER(destination, source) \ { (destination) = *(source) & 0377; \ (destination) += (*(signed char *)((source) + 1)) << 8; } /* Same as EXTRACT_NUMBER, except increment the pointer for source to point to second byte of SOURCE. Note that SOURCE has to be a value such as p, not, e.g., p + 1. */ #define EXTRACT_NUMBER_AND_INCR(destination, source) \ { EXTRACT_NUMBER (destination, source); \ (source) += 2; } /* Specify the precise syntax of regexps for compilation. This provides for compatibility for various utilities which historically have different, incompatible syntaxes. The argument SYNTAX is a bit-mask comprised of the various bits defined in regex.h. */ int a2_re_set_syntax (int syntax) { int ret; ret = obscure_syntax; obscure_syntax = syntax; return ret; } /* Macros for a2_re_compile_pattern, which is found below these definitions. */ #define CHAR_CLASS_MAX_LENGTH 6 /* Fetch the next character in the uncompiled pattern, translating it if necessary. */ #define PATFETCH(c) \ {if (p == pend) goto end_of_pattern; \ c = * (unsigned char *) p++; \ if (translate) c = translate[c]; } /* Fetch the next character in the uncompiled pattern, with no translation. */ #define PATFETCH_RAW(c) \ {if (p == pend) goto end_of_pattern; \ c = * (unsigned char *) p++; } #define PATUNFETCH p-- /* If the buffer isn't allocated when it comes in, use this. */ #define INIT_BUF_SIZE 28 /* Make sure we have at least N more bytes of space in buffer. */ #define GET_BUFFER_SPACE(n) \ { \ while (long(b - bufp->buffer + (n)) >= bufp->allocated) \ EXTEND_BUFFER; \ } /* Make sure we have one more byte of buffer space and then add CH to it. */ #define BUFPUSH(ch) \ { \ GET_BUFFER_SPACE (1); \ *b++ = (char) (ch); \ } /* Extend the buffer by twice its current size via reallociation and reset the pointers that pointed into the old allocation to point to the correct places in the new allocation. If extending the buffer results in it being larger than 1 << 16, then flag memory exhausted. */ #define EXTEND_BUFFER \ { char *old_buffer = bufp->buffer; \ if (bufp->allocated == (1L<<16)) goto too_big; \ bufp->allocated *= 2; \ if (bufp->allocated > (1L<<16)) bufp->allocated = (1L<<16); \ bufp->buffer = (char *) realloc (bufp->buffer, \ (unsigned int) bufp->allocated); \ if (bufp->buffer == 0) \ goto memory_exhausted; \ b = (b - old_buffer) + bufp->buffer; \ if (fixup_jump) \ fixup_jump = (fixup_jump - old_buffer) + bufp->buffer; \ if (laststart) \ laststart = (laststart - old_buffer) + bufp->buffer; \ begalt = (begalt - old_buffer) + bufp->buffer; \ if (pending_exact) \ pending_exact = (pending_exact - old_buffer) + bufp->buffer; \ } /* Set the bit for character C in a character set list. */ #define SET_LIST_BIT(c) (b[(c) / BYTEWIDTH] |= 1 << ((c) % BYTEWIDTH)) /* Get the next unsigned number in the uncompiled pattern. */ #define GET_UNSIGNED_NUMBER(num) \ { if (p != pend) \ { \ PATFETCH (c); \ while (isdigit (c)) \ { \ if (num < 0) \ num = 0; \ num = num * 10 + c - '0'; \ if (p == pend) \ break; \ PATFETCH (c); \ } \ } \ } /* Subroutines for a2_re_compile_pattern. */ static void store_jump (char *from, char opcode, char *to); static void insert_jump (char op, char *from, char *to, char *current_end); static void store_jump_n (char *from, char opcode, char *to, unsigned n); static void insert_jump_n (char, char *, char *, char *, unsigned); static void insert_op_2 (char, char *, char *_end, int, int); /* a2_re_compile_pattern takes a regular-expression string and converts it into a buffer full of byte commands for matching. PATTERN is the address of the pattern string SIZE is the length of it. BUFP is a struct re_pattern_buffer * which points to the info on where to store the byte commands. This structure contains a char * which points to the actual space, which should have been obtained with malloc. a2_re_compile_pattern may use realloc to grow the buffer space. The number of bytes of commands can be found out by looking in the `struct re_pattern_buffer' that bufp pointed to, after a2_re_compile_pattern returns. */ const char* a2_re_compile_pattern (char *pattern, int size, struct re_pattern_buffer *bufp) { char *b = bufp->buffer; char *p = pattern; char *pend = pattern + size; unsigned c, c1; char *p1; unsigned char *translate = (unsigned char *) bufp->translate; /* Address of the count-byte of the most recently inserted `exactn' command. This makes it possible to tell whether a new exact-match character can be added to that command or requires a new `exactn' command. */ char *pending_exact = 0; /* Address of the place where a forward-jump should go to the end of the containing expression. Each alternative of an `or', except the last, ends with a forward-jump of this sort. */ char *fixup_jump = 0; /* Address of start of the most recently finished expression. This tells postfix * where to find the start of its operand. */ char *laststart = 0; /* In processing a repeat, 1 means zero matches is allowed. */ char zero_times_ok; /* In processing a repeat, 1 means many matches is allowed. */ char many_times_ok; /* Address of beginning of regexp, or inside of last \(. */ char *begalt = b; /* In processing an interval, at least this many matches must be made. */ int lower_bound; /* In processing an interval, at most this many matches can be made. */ int upper_bound; /* Place in pattern (i.e., the {) to which to go back if the interval is invalid. */ char *beg_interval = 0; /* Stack of information saved by \( and restored by \). Four stack elements are pushed by each \(: First, the value of b. Second, the value of fixup_jump. Third, the value of regnum. Fourth, the value of begalt. */ int stackb[40]; int *stackp = stackb; int *stacke = stackb + 40; int *stackt; /* Counts \('s as they are encountered. Remembered for the matching \), where it becomes the register number to put in the stop_memory command. */ int regnum = 1; bufp->fastmap_accurate = 0; /* Initialize the syntax table. */ init_syntax_once(); if (bufp->allocated == 0) { bufp->allocated = INIT_BUF_SIZE; if (bufp->buffer) /* EXTEND_BUFFER loses when bufp->allocated is 0. */ bufp->buffer = (char *) realloc (bufp->buffer, INIT_BUF_SIZE); else /* Caller did not allocate a buffer. Do it for them. */ bufp->buffer = (char *) malloc (INIT_BUF_SIZE); if (!bufp->buffer) goto memory_exhausted; begalt = b = bufp->buffer; } while (p != pend) { PATFETCH (c); switch (c) { case '$': { char *p1 = p; /* When testing what follows the $, look past the \-constructs that don't consume anything. */ if (! (obscure_syntax & RE_CONTEXT_INDEP_OPS)) while (p1 != pend) { if (*p1 == '\\' && p1 + 1 != pend && (p1[1] == '<' || p1[1] == '>' || p1[1] == '`' || p1[1] == '\'' || p1[1] == 'b' || p1[1] == 'B')) p1 += 2; else break; } if (obscure_syntax & RE_TIGHT_VBAR) { if (! (obscure_syntax & RE_CONTEXT_INDEP_OPS) && p1 != pend) goto normal_char; /* Make operand of last vbar end before this `$'. */ if (fixup_jump) store_jump (fixup_jump, jump, b); fixup_jump = 0; BUFPUSH (endline); break; } /* $ means succeed if at end of line, but only in special contexts. If validly in the middle of a pattern, it is a normal character. */ if ((obscure_syntax & RE_CONTEXTUAL_INVALID_OPS) && p1 != pend) goto invalid_pattern; if (p1 == pend || *p1 == '\n' || (obscure_syntax & RE_CONTEXT_INDEP_OPS) || (obscure_syntax & RE_NO_BK_PARENS ? *p1 == ')' : *p1 == '\\' && p1[1] == ')') || (obscure_syntax & RE_NO_BK_VBAR ? *p1 == '|' : *p1 == '\\' && p1[1] == '|')) { BUFPUSH (endline); break; } goto normal_char; } case '^': /* ^ means succeed if at beg of line, but only if no preceding pattern. */ if ((obscure_syntax & RE_CONTEXTUAL_INVALID_OPS) && laststart) goto invalid_pattern; if (laststart && p - 2 >= pattern && p[-2] != '\n' && !(obscure_syntax & RE_CONTEXT_INDEP_OPS)) goto normal_char; if (obscure_syntax & RE_TIGHT_VBAR) { if (p != pattern + 1 && ! (obscure_syntax & RE_CONTEXT_INDEP_OPS)) goto normal_char; BUFPUSH (begline); begalt = b; } else BUFPUSH (begline); break; case '+': case '?': if ((obscure_syntax & RE_BK_PLUS_QM) || (obscure_syntax & RE_LIMITED_OPS)) goto normal_char; handle_plus: case '*': /* If there is no previous pattern, char not special. */ if (!laststart) { if (obscure_syntax & RE_CONTEXTUAL_INVALID_OPS) goto invalid_pattern; else if (! (obscure_syntax & RE_CONTEXT_INDEP_OPS)) goto normal_char; } /* If there is a sequence of repetition chars, collapse it down to just one. */ zero_times_ok = 0; many_times_ok = 0; while (1) { zero_times_ok |= c != '+'; many_times_ok |= c != '?'; if (p == pend) break; PATFETCH (c); if (c == '*') ; else if (!(obscure_syntax & RE_BK_PLUS_QM) && (c == '+' || c == '?')) ; else if ((obscure_syntax & RE_BK_PLUS_QM) && c == '\\') { int c1; PATFETCH (c1); if (!(c1 == '+' || c1 == '?')) { PATUNFETCH; PATUNFETCH; break; } c = c1; } else { PATUNFETCH; break; } } /* Star, etc. applied to an empty pattern is equivalent to an empty pattern. */ if (!laststart) break; /* Now we know whether or not zero matches is allowed and also whether or not two or more matches is allowed. */ if (many_times_ok) { /* If more than one repetition is allowed, put in at the end a backward relative jump from b to before the next jump we're going to put in below (which jumps from laststart to after this jump). */ GET_BUFFER_SPACE (3); store_jump (b,(char) maybe_finalize_jump, laststart - 3); b += 3; /* Because store_jump put stuff here. */ } /* On failure, jump from laststart to b + 3, which will be the end of the buffer after this jump is inserted. */ GET_BUFFER_SPACE (3); insert_jump (on_failure_jump, laststart, b + 3, b); pending_exact = 0; b += 3; if (!zero_times_ok) { /* At least one repetition is required, so insert a dummy-failure before the initial on-failure-jump instruction of the loop. This effects a skip over that instruction the first time we hit that loop. */ GET_BUFFER_SPACE (6); insert_jump (dummy_failure_jump, laststart, laststart + 6, b); b += 3; } break; case '.': laststart = b; BUFPUSH (anychar); break; case '[': if (p == pend) goto invalid_pattern; while (b - bufp->buffer > bufp->allocated - 3 - (1 << BYTEWIDTH) / BYTEWIDTH) EXTEND_BUFFER; laststart = b; if (*p == '^') { BUFPUSH (charset_not); p++; } else BUFPUSH (charset); p1 = p; BUFPUSH ((1 << BYTEWIDTH) / BYTEWIDTH); /* Clear the whole map */ memset (b, 0, (1 << BYTEWIDTH) / BYTEWIDTH); if ((obscure_syntax & RE_HAT_NOT_NEWLINE) && b[-2] == charset_not) SET_LIST_BIT ('\n'); /* Read in characters and ranges, setting map bits. */ while (1) { /* Don't translate while fetching, in case it's a range bound. When we set the bit for the character, we translate it. */ PATFETCH_RAW (c); /* If set, \ escapes characters when inside [...]. */ if ((obscure_syntax & RE_AWK_CLASS_HACK) && c == '\\') { PATFETCH(c1); SET_LIST_BIT (c1); continue; } if (c == ']') { if (p == p1 + 1) { /* If this is an empty bracket expression. */ if ((obscure_syntax & RE_NO_EMPTY_BRACKETS) && p == pend) goto invalid_pattern; } else /* Stop if this isn't merely a ] inside a bracket expression, but rather the end of a bracket expression. */ break; } /* Get a range. */ if (p[0] == '-' && p[1] != ']') { PATFETCH (c1); /* Don't translate the range bounds while fetching them. */ PATFETCH_RAW (c1); if ((obscure_syntax & RE_NO_EMPTY_RANGES) && c > c1) goto invalid_pattern; if ((obscure_syntax & RE_NO_HYPHEN_RANGE_END) && c1 == '-' && *p != ']') goto invalid_pattern; while (c <= c1) { /* Translate each char that's in the range. */ if (translate) SET_LIST_BIT (translate[c]); else SET_LIST_BIT (c); c++; } } else if ((obscure_syntax & RE_CHAR_CLASSES) && c == '[' && p[0] == ':') { /* Longest valid character class word has six characters. */ char str[CHAR_CLASS_MAX_LENGTH+1]; PATFETCH (c); c1 = 0; /* If no ] at end. */ if (p == pend) goto invalid_pattern; while (1) { /* Don't translate the ``character class'' characters. */ PATFETCH_RAW (c); if (c == ':' || c == ']' || p == pend || c1 == CHAR_CLASS_MAX_LENGTH) break; str[c1++] = c; } str[c1] = '\0'; if (p == pend || c == ']' /* End of the bracket expression. */ || p[0] != ']' || p + 1 == pend || (strcmp (str, "alpha") != 0 && strcmp (str, "upper") != 0 && strcmp (str, "lower") != 0 && strcmp (str, "digit") != 0 && strcmp (str, "alnum") != 0 && strcmp (str, "xdigit") != 0 && strcmp (str, "space") != 0 && strcmp (str, "print") != 0 && strcmp (str, "punct") != 0 && strcmp (str, "graph") != 0 && strcmp (str, "cntrl") != 0)) { /* Undo the ending character, the letters, and leave the leading : and [ (but set bits for them). */ c1++; while (c1--) PATUNFETCH; SET_LIST_BIT ('['); SET_LIST_BIT (':'); } else { /* The ] at the end of the character class. */ PATFETCH (c); if (c != ']') goto invalid_pattern; for (c = 0; c < (1 << BYTEWIDTH); c++) { if ((strcmp (str, "alpha") == 0 && isalpha (c)) || (strcmp (str, "upper") == 0 && isupper (c)) || (strcmp (str, "lower") == 0 && islower (c)) || (strcmp (str, "digit") == 0 && isdigit (c)) || (strcmp (str, "alnum") == 0 && isalnum (c)) || (strcmp (str, "xdigit") == 0 && isxdigit (c)) || (strcmp (str, "space") == 0 && isspace (c)) || (strcmp (str, "print") == 0 && isprint (c)) || (strcmp (str, "punct") == 0 && ispunct (c)) || (strcmp (str, "graph") == 0 && isgraph (c)) || (strcmp (str, "cntrl") == 0 && iscntrl (c))) SET_LIST_BIT (c); } } } else if (translate) SET_LIST_BIT (translate[c]); else SET_LIST_BIT (c); } /* Discard any character set/class bitmap bytes that are all 0 at the end of the map. Decrement the map-length byte too. */ while ((int) b[-1] > 0 && b[b[-1] - 1] == 0) b[-1]--; b += b[-1]; break; case '(': if (! (obscure_syntax & RE_NO_BK_PARENS)) goto normal_char; else goto handle_open; case ')': if (! (obscure_syntax & RE_NO_BK_PARENS)) goto normal_char; else goto handle_close; case '\n': if (! (obscure_syntax & RE_NEWLINE_OR)) goto normal_char; else goto handle_bar; case '|': if ((obscure_syntax & RE_CONTEXTUAL_INVALID_OPS) && (! laststart || p == pend)) goto invalid_pattern; else if (! (obscure_syntax & RE_NO_BK_VBAR)) goto normal_char; else goto handle_bar; case '{': if (! ((obscure_syntax & RE_NO_BK_CURLY_BRACES) && (obscure_syntax & RE_INTERVALS))) goto normal_char; else goto handle_interval; case '\\': if (p == pend) goto invalid_pattern; PATFETCH_RAW (c); switch (c) { case '(': if (obscure_syntax & RE_NO_BK_PARENS) goto normal_backsl; handle_open: if (stackp == stacke) goto nesting_too_deep; /* Laststart should point to the start_memory that we are about to push (unless the pattern has RE_NREGS or more ('s). */ *stackp++ = b - bufp->buffer; if (regnum >= RE_NREGS) goto too_many_paren; BUFPUSH (start_memory); BUFPUSH (regnum); *stackp++ = fixup_jump ? fixup_jump - bufp->buffer + 1 : 0; *stackp++ = regnum++; *stackp++ = begalt - bufp->buffer; fixup_jump = 0; laststart = 0; begalt = b; break; case ')': if (obscure_syntax & RE_NO_BK_PARENS) goto normal_backsl; handle_close: if (stackp == stackb) goto unmatched_close; begalt = *--stackp + bufp->buffer; if (fixup_jump) store_jump (fixup_jump, jump, b); if (stackp[-1] < RE_NREGS) { BUFPUSH (stop_memory); BUFPUSH (stackp[-1]); } stackp -= 2; fixup_jump = *stackp ? *stackp + bufp->buffer - 1 : 0; laststart = *--stackp + bufp->buffer; break; case '|': if ((obscure_syntax & RE_LIMITED_OPS) || (obscure_syntax & RE_NO_BK_VBAR)) goto normal_backsl; handle_bar: if (obscure_syntax & RE_LIMITED_OPS) goto normal_char; /* Insert before the previous alternative a jump which jumps to this alternative if the former fails. */ GET_BUFFER_SPACE (6); insert_jump (on_failure_jump, begalt, b + 6, b); pending_exact = 0; b += 3; /* The alternative before the previous alternative has a jump after it which gets executed if it gets matched. Adjust that jump so it will jump to the previous alternative's analogous jump (put in below, which in turn will jump to the next (if any) alternative's such jump, etc.). The last such jump jumps to the correct final destination. */ if (fixup_jump) store_jump (fixup_jump, jump, b); /* Leave space for a jump after previous alternative---to be filled in later. */ fixup_jump = b; b += 3; laststart = 0; begalt = b; break; case '{': if (! (obscure_syntax & RE_INTERVALS) /* Let \{ be a literal. */ || ((obscure_syntax & RE_INTERVALS) && (obscure_syntax & RE_NO_BK_CURLY_BRACES)) /* If it's the string "\{". */ || (p - 2 == pattern && p == pend)) goto normal_backsl; handle_interval: beg_interval = p - 1; /* The {. */ /* If there is no previous pattern, this isn't an interval. */ if (!laststart) { if (obscure_syntax & RE_CONTEXTUAL_INVALID_OPS) goto invalid_pattern; else goto normal_backsl; } /* It also isn't an interval if not preceded by an re matching a single character or subexpression, or if the current type of intervals can't handle back references and the previous thing is a back reference. */ if (! (*laststart == anychar || *laststart == charset || *laststart == charset_not || *laststart == start_memory || (*laststart == exactn && laststart[1] == 1) || (! (obscure_syntax & RE_NO_BK_REFS) && *laststart == duplicate))) { if (obscure_syntax & RE_NO_BK_CURLY_BRACES) goto normal_char; /* Posix extended syntax is handled in previous statement; this is for Posix basic syntax. */ if (obscure_syntax & RE_INTERVALS) goto invalid_pattern; goto normal_backsl; } lower_bound = -1; /* So can see if are set. */ upper_bound = -1; GET_UNSIGNED_NUMBER (lower_bound); if (c == ',') { GET_UNSIGNED_NUMBER (upper_bound); if (upper_bound < 0) upper_bound = RE_DUP_MAX; } if (upper_bound < 0) upper_bound = lower_bound; if (! (obscure_syntax & RE_NO_BK_CURLY_BRACES)) { if (c != '\\') goto invalid_pattern; PATFETCH (c); } if (c != '}' || lower_bound < 0 || upper_bound > RE_DUP_MAX || lower_bound > upper_bound || ((obscure_syntax & RE_NO_BK_CURLY_BRACES) && p != pend && *p == '{')) { if (obscure_syntax & RE_NO_BK_CURLY_BRACES) goto unfetch_interval; else goto invalid_pattern; } /* If upper_bound is zero, don't want to succeed at all; jump from laststart to b + 3, which will be the end of the buffer after this jump is inserted. */ if (upper_bound == 0) { GET_BUFFER_SPACE (3); insert_jump (jump, laststart, b + 3, b); b += 3; } /* Otherwise, after lower_bound number of succeeds, jump to after the jump_n which will be inserted at the end of the buffer, and insert that jump_n. */ else { /* Set to 5 if only one repetition is allowed and hence no jump_n is inserted at the current end of the buffer; then only space for the succeed_n is needed. Otherwise, need space for both the succeed_n and the jump_n. */ unsigned slots_needed = upper_bound == 1 ? 5 : 10; GET_BUFFER_SPACE (slots_needed); /* Initialize the succeed_n to n, even though it will be set by its attendant set_number_at, because a2_re_compile_fastmap will need to know it. Jump to what the end of buffer will be after inserting this succeed_n and possibly appending a jump_n. */ insert_jump_n (succeed_n, laststart, b + slots_needed, b, lower_bound); b += 5; /* Just increment for the succeed_n here. */ /* More than one repetition is allowed, so put in at the end of the buffer a backward jump from b to the succeed_n we put in above. By the time we've gotten to this jump when matching, we'll have matched once already, so jump back only upper_bound - 1 times. */ if (upper_bound > 1) { store_jump_n (b, jump_n, laststart, upper_bound - 1); b += 5; /* When hit this when matching, reset the preceding jump_n's n to upper_bound - 1. */ BUFPUSH (set_number_at); GET_BUFFER_SPACE (2); STORE_NUMBER_AND_INCR (b, -5); STORE_NUMBER_AND_INCR (b, upper_bound - 1); } /* When hit this when matching, set the succeed_n's n. */ GET_BUFFER_SPACE (5); insert_op_2 (set_number_at, laststart, b, 5, lower_bound); b += 5; } pending_exact = 0; beg_interval = 0; break; unfetch_interval: /* If an invalid interval, match the characters as literals. */ if (beg_interval) p = beg_interval; else { fprintf (stderr, "regex: no interval beginning to which to backtrack.\n"); exit (1); } beg_interval = 0; PATFETCH (c); /* normal_char expects char in `c'. */ goto normal_char; /** break; ** previous line commented out because: ** regex.cc, line 1046: warning: statement after goto not reached **/ case 'w': laststart = b; BUFPUSH (wordchar); break; case 'W': laststart = b; BUFPUSH (notwordchar); break; case '<': BUFPUSH (wordbeg); break; case '>': BUFPUSH (wordend); break; case 'b': BUFPUSH (wordbound); break; case 'B': BUFPUSH (notwordbound); break; case '`': BUFPUSH (begbuf); break; case '\'': BUFPUSH (endbuf); break; case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if (obscure_syntax & RE_NO_BK_REFS) goto normal_char; c1 = c - '0'; if (int(c1) >= regnum) { if (obscure_syntax & RE_NO_EMPTY_BK_REF) goto invalid_pattern; else goto normal_char; } /* Can't back reference to a subexpression if inside of it. */ for (stackt = stackp - 2; stackt > stackb; stackt -= 4) if (*stackt == int(c1)) goto normal_char; laststart = b; BUFPUSH (duplicate); BUFPUSH (c1); break; case '+': case '?': if (obscure_syntax & RE_BK_PLUS_QM) goto handle_plus; else goto normal_backsl; default: normal_backsl: /* You might think it would be useful for \ to mean not to translate; but if we don't translate it it will never match anything. */ if (translate) c = translate[c]; goto normal_char; } break; default: normal_char: /* Expects the character in `c'. */ if (!pending_exact || pending_exact + *pending_exact + 1 != b || *pending_exact == 0177 || *p == '*' || *p == '^' || ((obscure_syntax & RE_BK_PLUS_QM) ? *p == '\\' && (p[1] == '+' || p[1] == '?') : (*p == '+' || *p == '?')) || ((obscure_syntax & RE_INTERVALS) && ((obscure_syntax & RE_NO_BK_CURLY_BRACES) ? *p == '{' : (p[0] == '\\' && p[1] == '{')))) { laststart = b; BUFPUSH (exactn); pending_exact = b; BUFPUSH (0); } BUFPUSH (c); (*pending_exact)++; } } if (fixup_jump) store_jump (fixup_jump, jump, b); if (stackp != stackb) goto unmatched_open; bufp->used = b - bufp->buffer; return 0; invalid_pattern: static const char* invpatt = "Invalid regular expression"; return invpatt; unmatched_open: static const char* unmopen = "Unmatched \\("; return unmopen; unmatched_close: static const char* unmclose = "Unmatched \\)"; return unmclose; end_of_pattern: static const char* endpatt = "Premature end of regular expression"; return endpatt; nesting_too_deep: static const char* toodeep = "Nesting too deep"; return toodeep; too_many_paren: static const char* toomanyparen = "Too many parentheses"; return toomanyparen; too_big: static const char* toobig = "Regular expression too big"; return toobig; memory_exhausted: static const char* memexh = "Memory exhausted"; return memexh; } /* Store a jump of the form . Store in the location FROM a jump operation to jump to relative address FROM - TO. OPCODE is the opcode to store. */ static void store_jump (char *from, char opcode, char *to) { from[0] = opcode; STORE_NUMBER(from + 1, to - (from + 3)); } /* Open up space before char FROM, and insert there a jump to TO. CURRENT_END gives the end of the storage not in use, so we know how much data to copy up. OP is the opcode of the jump to insert. If you call this function, you must zero out pending_exact. */ static void insert_jump (char op, char *from, char *to, char *current_end) { char *pfrom = current_end; /* Copy from here... */ char *pto = current_end + 3; /* ...to here. */ while (pfrom != from) *--pto = *--pfrom; store_jump (from, op, to); } /* Store a jump of the form . Store in the location FROM a jump operation to jump to relative address FROM - TO. OPCODE is the opcode to store, N is a number the jump uses, say, to decide how many times to jump. If you call this function, you must zero out pending_exact. */ static void store_jump_n (char *from, char opcode, char *to, unsigned n) { from[0] = opcode; STORE_NUMBER (from + 1, to - (from + 3)); STORE_NUMBER (from + 3, n); } /* Similar to insert_jump, but handles a jump which needs an extra number to handle minimum and maximum cases. Open up space at location FROM, and insert there a jump to TO. CURRENT_END gives the end of the storage in use, so we know how much data to copy up. OP is the opcode of the jump to insert. If you call this function, you must zero out pending_exact. */ static void insert_jump_n (char op, char *from, char *to, char *current_end, unsigned n) { char *pfrom = current_end; /* Copy from here... */ char *pto = current_end + 5; /* ...to here. */ while (pfrom != from) *--pto = *--pfrom; store_jump_n (from, op, to, n); } /* Open up space at location THERE, and insert operation OP followed by NUM_1 and NUM_2. CURRENT_END gives the end of the storage in use, so we know how much data to copy up. If you call this function, you must zero out pending_exact. */ static void insert_op_2 (char op, char *there, char *current_end, int num_1, int num_2) { char *pfrom = current_end; /* Copy from here... */ char *pto = current_end + 5; /* ...to here. */ while (pfrom != there) *--pto = *--pfrom; there[0] = op; STORE_NUMBER (there + 1, num_1); STORE_NUMBER (there + 3, num_2); } /* Given a pattern, compute a fastmap from it. The fastmap records which of the (1 << BYTEWIDTH) possible characters can start a string that matches the pattern. This fastmap is used by a2_re_search to skip quickly over totally implausible text. The caller must supply the address of a (1 << BYTEWIDTH)-byte data area as bufp->fastmap. The other components of bufp describe the pattern to be used. */ void a2_re_compile_fastmap (struct re_pattern_buffer *bufp) { unsigned char *pattern = (unsigned char *) bufp->buffer; long size = bufp->used; char *fastmap = bufp->fastmap; unsigned char *p = pattern; unsigned char *pend = pattern + size; int j, k; unsigned char *translate = (unsigned char *) bufp->translate; unsigned char *stackb[NFAILURES]; unsigned char **stackp = stackb; unsigned is_a_succeed_n; memset (fastmap, 0, (1 << BYTEWIDTH)); bufp->fastmap_accurate = 1; bufp->can_be_null = 0; while (p) { is_a_succeed_n = 0; if (p == pend) { bufp->can_be_null = 1; break; } #ifdef SWITCH_ENUM_BUG switch ((int) ((enum regexpcode) *p++)) #else switch ((enum regexpcode) *p++) #endif { case exactn: if (translate) fastmap[translate[p[1]]] = 1; else fastmap[p[1]] = 1; break; case begline: case before_dot: case at_dot: case after_dot: case begbuf: case endbuf: case wordbound: case notwordbound: case wordbeg: case wordend: continue; case endline: if (translate) fastmap[translate[int('\n')]] = 1; else fastmap[int('\n')] = 1; if (bufp->can_be_null != 1) bufp->can_be_null = 2; break; case jump_n: case finalize_jump: case maybe_finalize_jump: case jump: case dummy_failure_jump: EXTRACT_NUMBER_AND_INCR (j, p); p += j; if (j > 0) continue; /* Jump backward reached implies we just went through the body of a loop and matched nothing. Opcode jumped to should be an on_failure_jump. Just treat it like an ordinary jump. For a * loop, it has pushed its failure point already; If so, discard that as redundant. */ if ((enum regexpcode) *p != on_failure_jump && (enum regexpcode) *p != succeed_n) continue; p++; EXTRACT_NUMBER_AND_INCR (j, p); p += j; if (stackp != stackb && *stackp == p) stackp--; continue; case on_failure_jump: handle_on_failure_jump: EXTRACT_NUMBER_AND_INCR (j, p); *++stackp = p + j; if (is_a_succeed_n) EXTRACT_NUMBER_AND_INCR (k, p); /* Skip the n. */ continue; case succeed_n: is_a_succeed_n = 1; /* Get to the number of times to succeed. */ p += 2; /* Increment p past the n for when k != 0. */ EXTRACT_NUMBER_AND_INCR (k, p); if (k == 0) { p -= 4; goto handle_on_failure_jump; } continue; case set_number_at: p += 4; continue; case start_memory: case stop_memory: p++; continue; case duplicate: bufp->can_be_null = 1; fastmap[int('\n')] = 1; case anychar: for (j = 0; j < (1 << BYTEWIDTH); j++) if (j != '\n') fastmap[j] = 1; if (bufp->can_be_null) return; /* Don't return; check the alternative paths so we can set can_be_null if appropriate. */ break; case wordchar: for (j = 0; j < (1 << BYTEWIDTH); j++) if (SYNTAX (j) == Sword) fastmap[j] = 1; break; case notwordchar: for (j = 0; j < (1 << BYTEWIDTH); j++) if (SYNTAX (j) != Sword) fastmap[j] = 1; break; case charset: for (j = *p++ * BYTEWIDTH - 1; j >= 0; j--) if (p[j / BYTEWIDTH] & (1 << (j % BYTEWIDTH))) { if (translate) fastmap[translate[j]] = 1; else fastmap[j] = 1; } break; case charset_not: /* Chars beyond end of map must be allowed */ for (j = *p * BYTEWIDTH; j < (1 << BYTEWIDTH); j++) if (translate) fastmap[translate[j]] = 1; else fastmap[j] = 1; for (j = *p++ * BYTEWIDTH - 1; j >= 0; j--) if (!(p[j / BYTEWIDTH] & (1 << (j % BYTEWIDTH)))) { if (translate) fastmap[translate[j]] = 1; else fastmap[j] = 1; } break; /** the following three cases were added to silence the compiler ** regex.cc, line 1330: warning: switch ( enum regexpcode ) with 28 cases ( 31 enumerators) **/ case syntaxspec: case notsyntaxspec: case unused: break; } /* Get here means we have successfully found the possible starting characters of one path of the pattern. We need not follow this path any farther. Instead, look at the next alternative remembered in the stack. */ if (stackp != stackb) p = *stackp--; else break; } } /* Like a2_re_search_2, below, but only one string is specified, and doesn't let you say where to stop matching. */ int a2_re_search (struct re_pattern_buffer *pbufp, char *string, int size, int startpos, int range, struct re_registers *regs) { return a2_re_search_2 (pbufp, static_cast(0), 0, string, size, startpos, range, regs, size); } /* Using the compiled pattern in PBUFP->buffer, first tries to match the virtual concatenation of STRING1 and STRING2, starting first at index STARTPOS, then at STARTPOS + 1, and so on. RANGE is the number of places to try before giving up. If RANGE is negative, it searches backwards, i.e., the starting positions tried are STARTPOS, STARTPOS - 1, etc. STRING1 and STRING2 are of SIZE1 and SIZE2, respectively. In REGS, return the indices of the virtual concatenation of STRING1 and STRING2 that matched the entire PBUFP->buffer and its contained subexpressions. Do not consider matching one past the index MSTOP in the virtual concatenation of STRING1 and STRING2. The value returned is the position in the strings at which the match was found, or -1 if no match was found, or -2 if error (such as failure stack overflow). */ int a2_re_search_2 (struct re_pattern_buffer *pbufp, char *string1, int size1, char *string2, int size2, int startpos, int range, struct re_registers *regs, int mstop) { char *fastmap = pbufp->fastmap; unsigned char *translate = (unsigned char *) pbufp->translate; int total_size = size1 + size2; int endpos = startpos + range; int val; /* Check for out-of-range starting position. */ if (startpos < 0 || startpos > total_size) return -1; /* Fix up range if it would eventually take startpos outside of the virtual concatenation of string1 and string2. */ if (endpos < -1) range = -1 - startpos; else if (endpos > total_size) range = total_size - startpos; /* Update the fastmap now if not correct already. */ if (fastmap && !pbufp->fastmap_accurate) a2_re_compile_fastmap (pbufp); /* If the search isn't to be a backwards one, don't waste time in a long search for a pattern that says it is anchored. */ if (pbufp->used > 0 && (enum regexpcode) pbufp->buffer[0] == begbuf && range > 0) { if (startpos > 0) return -1; else range = 1; } while (1) { /* If a fastmap is supplied, skip quickly over characters that cannot possibly be the start of a match. Note, however, that if the pattern can possibly match the null string, we must test it at each starting point so that we take the first null string we get. */ if (fastmap && startpos < total_size && pbufp->can_be_null != 1) { if (range > 0) /* Searching forwards. */ { int lim = 0; unsigned char *p; int irange = range; if (startpos < size1 && startpos + range >= size1) lim = range - (size1 - startpos); p = ((unsigned char *) &(startpos >= size1 ? string2 - size1 : string1)[startpos]); while (range > lim && !fastmap[translate ? translate[*p++] : *p++]) range--; startpos += irange - range; } else /* Searching backwards. */ { unsigned char c; if (string1 == 0 || startpos >= size1) c = string2[startpos - size1]; else c = string1[startpos]; c &= 0xff; if (translate ? !fastmap[translate[c]] : !fastmap[c]) goto advance; } } if (range >= 0 && startpos == total_size && fastmap && pbufp->can_be_null == 0) return -1; val = a2_re_match_2 (pbufp, string1, size1, string2, size2, startpos, regs, mstop); if (val >= 0) return startpos; if (val == -2) return -2; advance: if (!range) break; else if (range > 0) { range--; startpos++; } else { range++; startpos--; } } return -1; } int a2_re_match (struct re_pattern_buffer *pbufp, char *string, int size, int pos, struct re_registers *regs) { return a2_re_match_2 (pbufp, static_cast(0), 0, string, size, pos, regs, size); } /* The following are used for a2_re_match_2, defined below: */ /* Routine used by a2_re_match_2. */ static int bcmp_translate (char *, char *, int, unsigned char *); /* Structure and accessing macros used in a2_re_match_2: */ struct register_info { unsigned is_active : 1; unsigned matched_something : 1; }; #define IS_ACTIVE(R) ((R).is_active) #define MATCHED_SOMETHING(R) ((R).matched_something) /* Macros used by a2_re_match_2: */ /* I.e., regstart, regend, and reg_info. */ #define NUM_REG_ITEMS 3 /* We push at most this many things on the stack whenever we fail. The `+ 2' refers to PATTERN_PLACE and STRING_PLACE, which are arguments to the PUSH_FAILURE_POINT macro. */ #define MAX_NUM_FAILURE_ITEMS (RE_NREGS * NUM_REG_ITEMS + 2) /* We push this many things on the stack whenever we fail. */ #define NUM_FAILURE_ITEMS (last_used_reg * NUM_REG_ITEMS + 2) /* This pushes most of the information about the current state we will want if we ever fail back to it. */ #define PUSH_FAILURE_POINT(pattern_place, string_place) \ { \ long last_used_reg, this_reg; \ \ /* Find out how many registers are active or have been matched. \ (Aside from register zero, which is only set at the end.) */ \ for (last_used_reg = RE_NREGS - 1; last_used_reg > 0; last_used_reg--)\ if (regstart[last_used_reg] != (unsigned char *) -1) \ break; \ \ if (stacke - stackp < NUM_FAILURE_ITEMS) \ { \ unsigned char **stackx; \ unsigned int len = stacke - stackb; \ if (int(len) > re_max_failures * MAX_NUM_FAILURE_ITEMS) \ return -2; \ \ /* Roughly double the size of the stack. */ \ stackx = (unsigned char **) alloca (2 * len * sizeof (unsigned char *));\ /* Only copy what is in use. */ \ char* cpb=(char *)stackb;char* cpx=(char *)stackx; /* IBM! */ \ memcpy (cpx, cpb, len * sizeof (char *)); \ stackp = stackx + (stackp - stackb); \ stackb = stackx; \ stacke = stackb + 2 * len; \ } \ \ /* Now push the info for each of those registers. */ \ for (this_reg = 1; this_reg <= last_used_reg; this_reg++) \ { \ *stackp++ = regstart[this_reg]; \ *stackp++ = regend[this_reg]; \ *stackp++ = (unsigned char *) ®_info[this_reg]; \ } \ \ /* Push how many registers we saved. */ \ *stackp++ = (unsigned char *) last_used_reg; \ \ *stackp++ = pattern_place; \ *stackp++ = string_place; \ } /* This pops what PUSH_FAILURE_POINT pushes. */ #define POP_FAILURE_POINT() \ { \ long temp; \ stackp -= 2; /* Remove failure points. */ \ temp = (long) *--stackp; /* How many regs pushed. */ \ temp *= NUM_REG_ITEMS; /* How much to take off the stack. */ \ stackp -= temp; /* Remove the register info. */ \ } #define MATCHING_IN_FIRST_STRING (dend == end_match_1) /* Is true if there is a first string and if PTR is pointing anywhere inside it or just past the end. */ #define IS_IN_FIRST_STRING(ptr) \ (size1 && string1 <= (ptr) && (ptr) <= string1 + size1) /* Call before fetching a character with *d. This switches over to string2 if necessary. */ #define PREFETCH \ while (d == dend) \ { \ /* end of string2 => fail. */ \ if (dend == end_match_2) \ goto fail; \ /* end of string1 => advance to string2. */ \ d = string2; \ dend = end_match_2; \ } /* Call this when have matched something; it sets `matched' flags for the registers corresponding to the subexpressions of which we currently are inside. */ #define SET_REGS_MATCHED \ { unsigned this_reg; \ for (this_reg = 0; this_reg < RE_NREGS; this_reg++) \ { \ if (IS_ACTIVE(reg_info[this_reg])) \ MATCHED_SOMETHING(reg_info[this_reg]) = 1; \ else \ MATCHED_SOMETHING(reg_info[this_reg]) = 0; \ } \ } /* Test if at very beginning or at very end of the virtual concatenation of string1 and string2. If there is only one string, we've put it in string2. */ #define AT_STRINGS_BEG (d == (size1 ? string1 : string2) || !size2) #define AT_STRINGS_END (d == end2) #define AT_WORD_BOUNDARY \ (AT_STRINGS_BEG || AT_STRINGS_END || IS_A_LETTER (d - 1) != IS_A_LETTER (d)) /* We have two special cases to check for: 1) if we're past the end of string1, we have to look at the first character in string2; 2) if we're before the beginning of string2, we have to look at the last character in string1; we assume there is a string1, so use this in conjunction with AT_STRINGS_BEG. */ #define IS_A_LETTER(d) \ (SYNTAX ((d) == end1 ? *string2 : (d) == string2 - 1 ? *(end1 - 1) : *(d))\ == Sword) /* Match the pattern described by PBUFP against the virtual concatenation of STRING1 and STRING2, which are of SIZE1 and SIZE2, respectively. Start the match at index POS in the virtual concatenation of STRING1 and STRING2. In REGS, return the indices of the virtual concatenation of STRING1 and STRING2 that matched the entire PBUFP->buffer and its contained subexpressions. Do not consider matching one past the index MSTOP in the virtual concatenation of STRING1 and STRING2. If pbufp->fastmap is nonzero, then it had better be up to date. The reason that the data to match are specified as two components which are to be regarded as concatenated is so this function can be used directly on the contents of an Emacs buffer. -1 is returned if there is no match. -2 is returned if there is an error (such as match stack overflow). Otherwise the value is the length of the substring which was matched. */ int real_a2_re_match_2 (struct re_pattern_buffer *pbufp, char *string1_arg, int size1, char *string2_arg, int size2, int pos, struct re_registers *regs, int mstop, cregex_allocator &) { unsigned char *p = (unsigned char *) pbufp->buffer; /* Pointer to beyond end of buffer. */ unsigned char *pend = p + pbufp->used; unsigned char *string1 = (unsigned char *) string1_arg; unsigned char *string2 = (unsigned char *) string2_arg; unsigned char *end1; /* Just past end of first string. */ unsigned char *end2; /* Just past end of second string. */ /* Pointers into string1 and string2, just past the last characters in each to consider matching. */ unsigned char *end_match_1, *end_match_2; unsigned char *d, *dend; int mcnt; /* Multipurpose. */ unsigned char *translate = (unsigned char *) pbufp->translate; unsigned is_a_jump_n = 0; /* Failure point stack. Each place that can handle a failure further down the line pushes a failure point on this stack. It consists of restart, regend, and reg_info for all registers corresponding to the subexpressions we're currently inside, plus the number of such registers, and, finally, two char *'s. The first char * is where to resume scanning the pattern; the second one is where to resume scanning the strings. If the latter is zero, the failure point is a ``dummy''; if a failure happens and the failure point is a dummy, it gets discarded and the next next one is tried. */ unsigned char *initial_stack[MAX_NUM_FAILURE_ITEMS * NFAILURES]; unsigned char **stackb = initial_stack; unsigned char **stackp = stackb; unsigned char **stacke = &stackb[MAX_NUM_FAILURE_ITEMS * NFAILURES]; /* Information on the contents of registers. These are pointers into the input strings; they record just what was matched (on this attempt) by a subexpression part of the pattern, that is, the regnum-th regstart pointer points to where in the pattern we began matching and the regnum-th regend points to right after where we stopped matching the regnum-th subexpression. (The zeroth register keeps track of what the whole pattern matches.) */ unsigned char *regstart[RE_NREGS]; unsigned char *regend[RE_NREGS]; /* The is_active field of reg_info helps us keep track of which (possibly nested) subexpressions we are currently in. The matched_something field of reg_info[reg_num] helps us tell whether or not we have matched any of the pattern so far this time through the reg_num-th subexpression. These two fields get reset each time through any loop their register is in. */ struct register_info reg_info[RE_NREGS]; /* The following record the register info as found in the above variables when we find a match better than any we've seen before. This happens as we backtrack through the failure points, which in turn happens only if we have not yet matched the entire string. */ unsigned best_regs_set = 0; unsigned char *best_regstart[RE_NREGS]; unsigned char *best_regend[RE_NREGS]; /* Initialize subexpression text positions to -1 to mark ones that no \( or ( and \) or ) has been seen for. Also set all registers to inactive and mark them as not having matched anything or ever failed. */ for (mcnt = 0; mcnt < RE_NREGS; mcnt++) { regstart[mcnt] = regend[mcnt] = (unsigned char *) -1; IS_ACTIVE (reg_info[mcnt]) = 0; MATCHED_SOMETHING (reg_info[mcnt]) = 0; } if (regs) for (mcnt = 0; mcnt < RE_NREGS; mcnt++) regs->start[mcnt] = regs->end[mcnt] = -1; /* Set up pointers to ends of strings. Don't allow the second string to be empty unless both are empty. */ if (size2 == 0) { string2 = string1; size2 = size1; string1 = 0; size1 = 0; } end1 = string1 + size1; end2 = string2 + size2; /* Compute where to stop matching, within the two strings. */ if (mstop <= size1) { end_match_1 = string1 + mstop; end_match_2 = string2; } else { end_match_1 = end1; end_match_2 = string2 + mstop - size1; } /* `p' scans through the pattern as `d' scans through the data. `dend' is the end of the input string that `d' points within. `d' is advanced into the following input string whenever necessary, but this happens before fetching; therefore, at the beginning of the loop, `d' can be pointing at the end of a string, but it cannot equal string2. */ if (size1 != 0 && pos <= size1) d = string1 + pos, dend = end_match_1; else d = string2 + pos - size1, dend = end_match_2; /* This loops over pattern commands. It exits by returning from the function if match is complete, or it drops through if match fails at this starting point in the input data. */ while (1) { is_a_jump_n = 0; /* End of pattern means we might have succeeded. */ if (p == pend) { /* If not end of string, try backtracking. Otherwise done. */ if (d != end_match_2) { if (stackp != stackb) { /* More failure points to try. */ unsigned in_same_string = IS_IN_FIRST_STRING (best_regend[0]) == MATCHING_IN_FIRST_STRING; /* If exceeds best match so far, save it. */ if (! best_regs_set || (in_same_string && d > best_regend[0]) || (! in_same_string && ! MATCHING_IN_FIRST_STRING)) { best_regs_set = 1; best_regstart[0] = (unsigned char *) -1; best_regend[0] = d; /* Never use regstart[0]. */ for (mcnt = 1; mcnt < RE_NREGS; mcnt++) { best_regstart[mcnt] = regstart[mcnt]; best_regend[mcnt] = regend[mcnt]; } } goto fail; } /* If no failure points, don't restore garbage. */ else if (best_regs_set) { restore_best_regs: /* Restore best match. */ d = best_regend[0]; for (mcnt = 0; mcnt < RE_NREGS; mcnt++) { regstart[mcnt] = best_regstart[mcnt]; regend[mcnt] = best_regend[mcnt]; } } } /* If caller wants register contents data back, convert it to indices. */ if (regs) { regs->start[0] = pos; if (MATCHING_IN_FIRST_STRING) regs->end[0] = d - string1; else regs->end[0] = d - string2 + size1; for (mcnt = 1; mcnt < RE_NREGS; mcnt++) { if (regend[mcnt] == (unsigned char *) -1) { regs->start[mcnt] = -1; regs->end[mcnt] = -1; continue; } if (IS_IN_FIRST_STRING (regstart[mcnt])) regs->start[mcnt] = regstart[mcnt] - string1; else regs->start[mcnt] = regstart[mcnt] - string2 + size1; if (IS_IN_FIRST_STRING (regend[mcnt])) regs->end[mcnt] = regend[mcnt] - string1; else regs->end[mcnt] = regend[mcnt] - string2 + size1; } } return d - pos - (MATCHING_IN_FIRST_STRING ? string1 : string2 - size1); } /* Otherwise match next pattern command. */ #ifdef SWITCH_ENUM_BUG switch ((int) ((enum regexpcode) *p++)) #else switch ((enum regexpcode) *p++) #endif { /* \( [or `(', as appropriate] is represented by start_memory, \) by stop_memory. Both of those commands are followed by a register number in the next byte. The text matched within the \( and \) is recorded under that number. */ case start_memory: regstart[*p] = d; IS_ACTIVE (reg_info[*p]) = 1; MATCHED_SOMETHING (reg_info[*p]) = 0; p++; break; case stop_memory: regend[*p] = d; IS_ACTIVE (reg_info[*p]) = 0; /* If just failed to match something this time around with a sub- expression that's in a loop, try to force exit from the loop. */ if ((! MATCHED_SOMETHING (reg_info[*p]) || (enum regexpcode) p[-3] == start_memory) && (p + 1) != pend) { unsigned char *p2 = p + 1; mcnt = 0; switch (*p2++) { case jump_n: is_a_jump_n = 1; case finalize_jump: case maybe_finalize_jump: case jump: case dummy_failure_jump: EXTRACT_NUMBER_AND_INCR (mcnt, p2); if (is_a_jump_n) p2 += 2; break; } p2 += mcnt; /* If the next operation is a jump backwards in the pattern to an on_failure_jump, exit from the loop by forcing a failure after pushing on the stack the on_failure_jump's jump in the pattern, and d. */ if (mcnt < 0 && (enum regexpcode) *p2++ == on_failure_jump) { EXTRACT_NUMBER_AND_INCR (mcnt, p2); PUSH_FAILURE_POINT (p2 + mcnt, d); goto fail; } } p++; break; /* \ has been turned into a `duplicate' command which is followed by the numeric value of as the register number. */ case duplicate: { int regno = *p++; /* Get which register to match against */ unsigned char *d2, *dend2; /* Where in input to try to start matching. */ d2 = regstart[regno]; /* Where to stop matching; if both the place to start and the place to stop matching are in the same string, then set to the place to stop, otherwise, for now have to use the end of the first string. */ dend2 = ((IS_IN_FIRST_STRING (regstart[regno]) == IS_IN_FIRST_STRING (regend[regno])) ? regend[regno] : end_match_1); while (1) { /* If necessary, advance to next segment in register contents. */ while (d2 == dend2) { if (dend2 == end_match_2) break; if (dend2 == regend[regno]) break; d2 = string2, dend2 = regend[regno]; /* end of string1 => advance to string2. */ } /* At end of register contents => success */ if (d2 == dend2) break; /* If necessary, advance to next segment in data. */ PREFETCH; /* How many characters left in this segment to match. */ mcnt = dend - d; /* Want how many consecutive characters we can match in one shot, so, if necessary, adjust the count. */ if (mcnt > dend2 - d2) mcnt = dend2 - d2; /* Compare that many; failure if mismatch, else move past them. */ char *cpp1=(char *)d; char *cpp2=(char *)d2; if (translate /* IBM! COMPATIBLILITY */ ? bcmp_translate (cpp1, (char *)d2, mcnt, translate) : memcmp (cpp1, cpp2, mcnt)) goto fail; d += mcnt, d2 += mcnt; } } break; case anychar: PREFETCH; /* Fetch a data character. */ /* Match anything but a newline, maybe even a null. */ if ((translate ? translate[*d] : *d) == '\n' || ((obscure_syntax & RE_DOT_NOT_NULL) && (translate ? translate[*d] : *d) == '\000')) goto fail; SET_REGS_MATCHED; d++; break; case charset: case charset_not: { int NOT = 0; /* Nonzero for charset_not. */ int c; if (*(p - 1) == (unsigned char) charset_not) NOT = 1; PREFETCH; /* Fetch a data character. */ if (translate) c = translate[*d]; else c = *d; if (c < (int)(*p * BYTEWIDTH) && p[1 + c / BYTEWIDTH] & (1 << (c % BYTEWIDTH))) NOT = !NOT; p += 1 + *p; if (!NOT) goto fail; SET_REGS_MATCHED; d++; break; } case begline: if ((size1 != 0 && d == string1) || (size1 == 0 && size2 != 0 && d == string2) || (d && d[-1] == '\n') || (size1 == 0 && size2 == 0)) break; else goto fail; case endline: if (d == end2 || (d == end1 ? (size2 == 0 || *string2 == '\n') : *d == '\n')) break; goto fail; /* `or' constructs are handled by starting each alternative with an on_failure_jump that points to the start of the next alternative. Each alternative except the last ends with a jump to the joining point. (Actually, each jump except for the last one really jumps to the following jump, because tensioning the jumps is a hassle.) */ /* The start of a stupid repeat has an on_failure_jump that points past the end of the repeat text. This makes a failure point so that on failure to match a repetition, matching restarts past as many repetitions have been found with no way to fail and look for another one. */ /* A smart repeat is similar but loops back to the on_failure_jump so that each repetition makes another failure point. */ case on_failure_jump: on_failure: EXTRACT_NUMBER_AND_INCR (mcnt, p); PUSH_FAILURE_POINT (p + mcnt, d); break; /* The end of a smart repeat has a maybe_finalize_jump back. Change it either to a finalize_jump or an ordinary jump. */ case maybe_finalize_jump: EXTRACT_NUMBER_AND_INCR (mcnt, p); { unsigned char *p2 = p; /* Compare what follows with the beginning of the repeat. If we can establish that there is nothing that they would both match, we can change to finalize_jump. */ while (p2 + 1 < pend && (*p2 == (unsigned char) stop_memory || *p2 == (unsigned char) start_memory)) p2 += 2; /* Skip over reg number. */ if (p2 == pend) p[-3] = (unsigned char) finalize_jump; else if (*p2 == (unsigned char) exactn || *p2 == (unsigned char) endline) { int c = *p2 == (unsigned char) endline ? '\n' : p2[2]; unsigned char *p1 = p + mcnt; /* p1[0] ... p1[2] are an on_failure_jump. Examine what follows that. */ if (p1[3] == (unsigned char) exactn && p1[5] != c) p[-3] = (unsigned char) finalize_jump; else if (p1[3] == (unsigned char) charset || p1[3] == (unsigned char) charset_not) { int NOT = p1[3] == (unsigned char) charset_not; if (c < (int)(p1[4] * BYTEWIDTH) && p1[5 + c / BYTEWIDTH] & (1 << (c % BYTEWIDTH))) NOT = !NOT; /* `not' is 1 if c would match. */ /* That means it is not safe to finalize. */ if (!NOT) p[-3] = (unsigned char) finalize_jump; } } } p -= 2; /* Point at relative address again. */ if (p[-1] != (unsigned char) finalize_jump) { p[-1] = (unsigned char) jump; goto nofinalize; } /* Note fall through. */ /* The end of a stupid repeat has a finalize_jump back to the start, where another failure point will be made which will point to after all the repetitions found so far. */ /* Take off failure points put on by matching on_failure_jump because didn't fail. Also remove the register information put on by the on_failure_jump. */ case finalize_jump: POP_FAILURE_POINT (); /* Note fall through. */ /* Jump without taking off any failure points. */ case jump: nofinalize: EXTRACT_NUMBER_AND_INCR (mcnt, p); p += mcnt; break; case dummy_failure_jump: /* Normally, the on_failure_jump pushes a failure point, which then gets popped at finalize_jump. We will end up at finalize_jump, also, and with a pattern of, say, `a+', we are skipping over the on_failure_jump, so we have to push something meaningless for finalize_jump to pop. */ PUSH_FAILURE_POINT (0, 0); goto nofinalize; /* Have to succeed matching what follows at least n times. Then just handle like an on_failure_jump. */ case succeed_n: EXTRACT_NUMBER (mcnt, p + 2); /* Originally, this is how many times we HAVE to succeed. */ if (mcnt) { mcnt--; p += 2; STORE_NUMBER_AND_INCR (p, mcnt); } else if (mcnt == 0) { p[2] = unused; p[3] = unused; goto on_failure; } else { fprintf (stderr, "regex: the succeed_n's n is not set.\n"); exit (1); } break; case jump_n: EXTRACT_NUMBER (mcnt, p + 2); /* Originally, this is how many times we CAN jump. */ if (mcnt) { mcnt--; STORE_NUMBER(p + 2, mcnt); goto nofinalize; /* Do the jump without taking off any failure points. */ } /* If don't have to jump any more, skip over the rest of command. */ else p += 4; break; case set_number_at: { unsigned char *p1; EXTRACT_NUMBER_AND_INCR (mcnt, p); p1 = p + mcnt; EXTRACT_NUMBER_AND_INCR (mcnt, p); STORE_NUMBER (p1, mcnt); break; } /* Ignore these. Used to ignore the n of succeed_n's which currently have n == 0. */ case unused: break; case wordbound: if (AT_WORD_BOUNDARY) break; goto fail; case notwordbound: if (AT_WORD_BOUNDARY) goto fail; break; case wordbeg: /* Have to check if AT_STRINGS_BEG before looking at d - 1. */ if (IS_A_LETTER (d) && (AT_STRINGS_BEG || !IS_A_LETTER (d - 1))) break; goto fail; case wordend: /* Have to check if AT_STRINGS_BEG before looking at d - 1. */ if (!AT_STRINGS_BEG && IS_A_LETTER (d - 1) && (!IS_A_LETTER (d) || AT_STRINGS_END)) break; goto fail; case wordchar: PREFETCH; if (!IS_A_LETTER (d)) goto fail; SET_REGS_MATCHED; break; case notwordchar: PREFETCH; if (IS_A_LETTER (d)) goto fail; SET_REGS_MATCHED; break; case begbuf: if (AT_STRINGS_BEG) break; goto fail; case endbuf: if (AT_STRINGS_END) break; goto fail; case exactn: /* Match the next few pattern characters exactly. mcnt is how many characters to match. */ mcnt = *p++; /* This is written out as an if-else so we don't waste time testing `translate' inside the loop. */ if (translate) { do { PREFETCH; if (translate[*d++] != *p++) goto fail; } while (--mcnt); } else { do { PREFETCH; if (*d++ != *p++) goto fail; } while (--mcnt); } SET_REGS_MATCHED; break; default: { fprintf (stderr, "regex: internal error; unknown case label\n"); exit (1); } } continue; /* Successfully executed one pattern command; keep going. */ /* Jump here if any matching operation fails. */ fail: if (stackp != stackb) /* A restart point is known. Restart there and pop it. */ { long last_used_reg, this_reg; /* If this failure point is from a dummy_failure_point, just skip it. */ if (!stackp[-2]) { POP_FAILURE_POINT (); goto fail; } d = *--stackp; p = *--stackp; if (d >= string1 && d <= end1) dend = end_match_1; /* Restore register info. */ // last_used_reg = (short) *--stackp; last_used_reg = (long) *--stackp; /* Make the ones that weren't saved -1 or 0 again. */ for (this_reg = RE_NREGS - 1; this_reg > last_used_reg; this_reg--) { regend[this_reg] = (unsigned char *) -1; regstart[this_reg] = (unsigned char *) -1; IS_ACTIVE (reg_info[this_reg]) = 0; MATCHED_SOMETHING (reg_info[this_reg]) = 0; } /* And restore the rest from the stack. */ for ( ; this_reg > 0; this_reg--) { reg_info[this_reg] = *(struct register_info *) *--stackp; regend[this_reg] = *--stackp; regstart[this_reg] = *--stackp; } } else break; /* Matching at this starting point really fails. */ } if (best_regs_set) goto restore_best_regs; return -1; /* Failure to match. */ } int a2_re_match_2 (struct re_pattern_buffer *pbufp, char *string1_arg, int size1, char *string2_arg, int size2, int pos, struct re_registers *regs, int mstop) { // Wrap the real implementation in a function since CFront doesn't like // to have "goto" and objects with destructors in the same block. cregex_allocator alloca; return real_a2_re_match_2 (pbufp, string1_arg, size1, string2_arg, size2, pos, regs, mstop, alloca); } static int bcmp_translate (char *s1, char *s2, int len, unsigned char *translate) { // Use signed char instead of char to avoid compiler warnings // about subscripting with a char. signed char *p1 = (signed char*)s1, *p2 = (signed char*)s2; while (len) { if (translate [*p1++] != translate [*p2++]) return 1; len--; } return 0; } /* Entry points compatible with 4.2 BSD regex library. */ static struct re_pattern_buffer re_comp_buf; const char* re_comp (char *s) { static const char* noprev = "No previous regular expression"; static const char* memexh = "Memory exhausted"; if (!s) { if (!re_comp_buf.buffer) { return noprev; } return 0; } if (!re_comp_buf.buffer) { if (!(re_comp_buf.buffer = (char *) malloc (200))) { return memexh; } re_comp_buf.allocated = 200; if (!(re_comp_buf.fastmap = new char[1 << BYTEWIDTH])) { return memexh; } } return a2_re_compile_pattern (s, strlen (s), &re_comp_buf); } int re_exec (char *s) { int len = strlen (s); return 0 <= a2_re_search (&re_comp_buf, s, len, 0, len, static_cast(0)); } } //# NAMESPACE CASACORE - END casacore-2.4.1/casa/Utilities/cregex.h000066400000000000000000000260201321422335000176010ustar00rootroot00000000000000/* cregex.h: Extended regular expression matching and search 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: aips2-request@nrao.edu. Postal address: AIPS++ Project Office National Radio Astronomy Observatory 520 Edgemont Road Charlottesville, VA 22903-2475 USA */ //# $Id$ #ifndef CASA_CREGEX_H #define CASA_CREGEX_H /* Definitions for data structures callers pass the regex library. */ #ifdef __cplusplus extern "C" { #endif /* Define number of parens for which we record the beginnings and ends. This affects how much space the `struct re_registers' type takes up. */ #ifndef RE_NREGS #define RE_NREGS 32 #endif #define BYTEWIDTH 8 #include namespace casacore { //# NAMESPACE CASACORE - BEGIN /* Maximum number of duplicates an interval can allow. */ /* Has been changed to copy from the limits.h file #if defined(_AIX) || defined(__sgi) # undef RE_DUP_MAX #endif #define RE_DUP_MAX ((1 << 15) - 1) */ /* This defines the various regexp syntaxes. It can be set using the function a2_re_set_syntax. */ ////extern int obscure_syntax; /* The following bits are used in the obscure_syntax variable to choose among alternative regexp syntaxes. */ /* If this bit is set, plain parentheses serve as grouping, and backslash parentheses are needed for literal searching. If not set, backslash-parentheses are grouping, and plain parentheses are for literal searching. */ #define RE_NO_BK_PARENS 1 /* If this bit is set, plain | serves as the `or'-operator, and \| is a literal. If not set, \| serves as the `or'-operator, and | is a literal. */ #define RE_NO_BK_VBAR (1 << 1) /* If this bit is not set, plain + or ? serves as an operator, and \+, \? are literals. If set, \+, \? are operators and plain +, ? are literals. */ #define RE_BK_PLUS_QM (1 << 2) /* If this bit is set, | binds tighter than ^ or $. If not set, the contrary. */ #define RE_TIGHT_VBAR (1 << 3) /* If this bit is set, then treat newline as an OR operator. If not set, treat it as a normal character. */ #define RE_NEWLINE_OR (1 << 4) /* If this bit is set, then special characters may act as normal characters in some contexts. Specifically, this applies to: ^ -- only special at the beginning, or after ( or |; $ -- only special at the end, or before ) or |; *, +, ? -- only special when not after the beginning, (, or |. If this bit is not set, special characters (such as *, ^, and $) always have their special meaning regardless of the surrounding context. */ #define RE_CONTEXT_INDEP_OPS (1 << 5) /* If this bit is not set, then \ before anything inside [ and ] is taken as a real \. If set, then such a \ escapes the following character. This is a special case for awk. */ #define RE_AWK_CLASS_HACK (1 << 6) /* If this bit is set, then \{ and \} or { and } serve as interval operators. If not set, then \{ and \} and { and } are treated as literals. */ #define RE_INTERVALS (1 << 7) /* If this bit is not set, then \{ and \} serve as interval operators and { and } are literals. If set, then { and } serve as interval operators and \{ and \} are literals. */ #define RE_NO_BK_CURLY_BRACES (1 << 8) /* If this bit is set, then character classes are supported; they are: [:alpha:], [:upper:], [:lower:], [:digit:], [:alnum:], [:xdigit:], [:space:], [:print:], [:punct:], [:graph:], and [:cntrl:]. If not set, then character classes are not supported. */ #define RE_CHAR_CLASSES (1 << 9) /* If this bit is set, then the dot re doesn't match a null byte. If not set, it does. */ #define RE_DOT_NOT_NULL (1 << 10) /* If this bit is set, then [^...] doesn't match a newline. If not set, it does. */ #define RE_HAT_NOT_NEWLINE (1 << 11) /* If this bit is not set, back references are recognized. If set, they aren't. */ #define RE_NO_BK_REFS (1 << 12) /* If this bit is set, back references must refer to a preceding subexpression. If not set, a back reference to a nonexistent subexpression is treated as literal characters. */ #define RE_NO_EMPTY_BK_REF (1 << 13) /* If this bit is set, bracket expressions can't be empty. If it is set, they can be empty. */ #define RE_NO_EMPTY_BRACKETS (1 << 14) /* If this bit is set, then *, +, ? and { cannot be first in an re or immediately after a |, or a (. Furthermore, a | cannot be first or last in an re, or immediately follow another | or a (. Also, a ^ cannot appear in a nonleading position and a $ cannot appear in a nontrailing position (outside of bracket expressions, that is). */ #define RE_CONTEXTUAL_INVALID_OPS (1 << 15) /* If this bit is set, then +, ? and | aren't recognized as operators. If it's not, they are. */ #define RE_LIMITED_OPS (1 << 16) /* If this bit is set, then an ending range point has to collate higher or equal to the starting range point. If it's not set, then when the ending range point collates higher than the starting range point, the range is just considered empty. */ #define RE_NO_EMPTY_RANGES (1 << 17) /* If this bit is set, then a hyphen (-) can't be an ending range point. If it isn't, then it can. */ #define RE_NO_HYPHEN_RANGE_END (1 << 18) /* Define combinations of bits for the standard possibilities. */ #define RE_SYNTAX_POSIX_AWK (RE_NO_BK_PARENS | RE_NO_BK_VBAR \ | RE_CONTEXT_INDEP_OPS) #define RE_SYNTAX_AWK (RE_NO_BK_PARENS | RE_NO_BK_VBAR \ | RE_CONTEXT_INDEP_OPS | RE_AWK_CLASS_HACK) #define RE_SYNTAX_EGREP (RE_NO_BK_PARENS | RE_NO_BK_VBAR \ | RE_CONTEXT_INDEP_OPS | RE_NEWLINE_OR) #define RE_SYNTAX_GREP (RE_BK_PLUS_QM | RE_NEWLINE_OR) #define RE_SYNTAX_EMACS 0 #define RE_SYNTAX_POSIX_BASIC (RE_INTERVALS | RE_BK_PLUS_QM \ | RE_CHAR_CLASSES | RE_DOT_NOT_NULL \ | RE_HAT_NOT_NEWLINE | RE_NO_EMPTY_BK_REF \ | RE_NO_EMPTY_BRACKETS | RE_LIMITED_OPS \ | RE_NO_EMPTY_RANGES | RE_NO_HYPHEN_RANGE_END) #define RE_SYNTAX_POSIX_EXTENDED (RE_INTERVALS | RE_NO_BK_CURLY_BRACES \ | RE_NO_BK_VBAR | RE_NO_BK_PARENS \ | RE_HAT_NOT_NEWLINE | RE_CHAR_CLASSES \ | RE_NO_EMPTY_BRACKETS | RE_CONTEXTUAL_INVALID_OPS \ | RE_NO_BK_REFS | RE_NO_EMPTY_RANGES \ | RE_NO_HYPHEN_RANGE_END) /* This data structure is used to represent a compiled pattern. */ // // This data structure is used to represent a compiled pattern. // // // // // This data structure is used to represent a compiled pattern. // It is used by the regular expression functions in cregex.cc. // struct re_pattern_buffer { char *buffer; /* Space holding the compiled pattern commands. */ long allocated; /* Size of space that `buffer' points to. */ long used; /* Length of portion of buffer actually occupied */ char *fastmap; /* Pointer to fastmap, if any, or zero if none. */ /* a2_re_search uses the fastmap, if there is one, to skip over totally implausible characters. */ char *translate; /* Translate table to apply to all characters before comparing, or zero for no translation. The translation is applied to a pattern when it is compiled and to data when it is matched. */ char fastmap_accurate; /* Set to zero when a new pattern is stored, set to one when the fastmap is updated from it. */ char can_be_null; /* Set to one by compiling fastmap if this pattern might match the null string. It does not necessarily match the null string in that case, but if this is zero, it cannot. 2 as value means can match null string but at end of range or before a character listed in the fastmap. */ }; /* search.c (search_buffer) needs this one value. It is defined both in regex.c and here. */ #define RE_EXACTN_VALUE 1 /* Structure to store register contents data in. Pass the address of such a structure as an argument to a2_re_match, etc., if you want this information back. For i from 1 to RE_NREGS - 1, start[i] records the starting index in the string of where the ith subexpression matched, and end[i] records one after the ending index. start[0] and end[0] are analogous, for the entire pattern. */ // // Data structure to store register contents data in. // // // // // This data structure is used to store register contents data in. // It is used by the regular expression functions in cregex.cc. // struct re_registers { int start[RE_NREGS]; int end[RE_NREGS]; }; #if defined(__STDC__) || defined(__cplusplus) extern const char *a2_re_compile_pattern (char *, int, struct re_pattern_buffer *); extern int a2_re_set_syntax (int syntax); /* Is this really advertised? */ extern void a2_re_compile_fastmap (struct re_pattern_buffer *); extern int a2_re_search (struct re_pattern_buffer *, char*, int, int, int, struct re_registers *); extern int a2_re_search_2 (struct re_pattern_buffer *, char *, int, char *, int, int, int, struct re_registers *, int); extern int a2_re_match (struct re_pattern_buffer *, char *, int, int, struct re_registers *); extern int a2_re_match_2 (struct re_pattern_buffer *, char *, int, char *, int, int, struct re_registers *, int); /* 4.2 bsd compatibility. */ // extern const char *re_comp (char *); // extern int re_exec (char *); #else /* !__STDC__ */ extern const char *a2_re_compile_pattern (); /* Is this really advertised? */ extern void a2_re_compile_fastmap (); extern int a2_re_search (), a2_re_search_2 (); extern int a2_re_match (), a2_re_match_2 (); /* 4.2 bsd compatibility. */ extern const char *re_comp (); extern int re_exec (); #endif /* __STDC__ */ #ifdef SYNTAX_TABLE ////extern char *re_syntax_table; #endif #ifdef __cplusplus ////extern int re_max_failures; } #endif } //# NAMESPACE CASACORE - END #endif /* !__REGEXP_LIBRARY */ casacore-2.4.1/casa/Utilities/generic.h000066400000000000000000000042131321422335000177400ustar00rootroot00000000000000//# 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. //# $Id$ #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-2.4.1/casa/Utilities/test/000077500000000000000000000000001321422335000171325ustar00rootroot00000000000000casacore-2.4.1/casa/Utilities/test/CMakeLists.txt000066400000000000000000000007621321422335000216770ustar00rootroot00000000000000set (tests dCOWptr dMUString tBinarySearch tBitVector tCompare tCompositeNumber tCopy tCountedPtr tCOWPtr tDataType tDefaultValue tDynBuffer tFallible tGenSort tLinearSearch tPrecision tPtrHolder tRegex_1 tRegex2 tRegex tRegister tSort_1 tSort tStringDistance ) foreach (test ${tests}) add_executable (${test} ${test}.cc) target_link_libraries (${test} casa_casa) add_test (${test} ${CMAKE_SOURCE_DIR}/cmake/cmake_assay ./${test}) add_dependencies(check ${test}) endforeach (test) casacore-2.4.1/casa/Utilities/test/dCOWptr.cc000066400000000000000000000067661321422335000210020ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/casa/Utilities/test/dMUString.cc000066400000000000000000000075301321422335000213220ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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 (AipsError x) { cout << x.getMesg() << endl; } try { String in = "12.13.15.9p"; Quantity res; cout << MVAngle::read(res, in) << endl; cout << in << " = " << res << endl; } catch (AipsError x) { cout << x.getMesg() << 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 (AipsError x) { cout << x.getMesg() << endl; } return(0); } casacore-2.4.1/casa/Utilities/test/tBinarySearch.cc000066400000000000000000000151011321422335000221750ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/casa/Utilities/test/tBitVector.cc000066400000000000000000000105011321422335000215230ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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); // test setReadOnly if (!deleteIt) { copy.setReadOnly(foo); AlwaysAssert(allEQ(*copy, *foo), AipsError); AlwaysAssert(copy.isReadOnly(), 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); // the following can be uncommented when the CountedPtr class // is made to set the deleted pointer to NULL. // AlwaysAssert(!ptr, 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); // the following can be uncommented when the CountedPtr class // is made to set the deleted pointer to NULL. // AlwaysAssert(!ptr, 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 (AipsError x) { cerr << x.getMesg () << endl; } return 0; } casacore-2.4.1/casa/Utilities/test/tCompare.cc000066400000000000000000000101561321422335000212160ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: tSort.cc 21299 2013-01-08 09:05:20Z gervandiepen $ //# 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"; CountedPtr 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-2.4.1/casa/Utilities/test/tCopy.cc000066400000000000000000000267761321422335000205610ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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-2.4.1/casa/Utilities/test/tCountedPtr.cc000066400000000000000000000074021321422335000217170ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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-2.4.1/casa/Utilities/test/tCountedPtr.h000066400000000000000000000047401321422335000215630ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ // 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. //
      • 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-2.4.1/casa/Utilities/test/tCountedPtr.out000066400000000000000000000013251321422335000221370ustar00rootroot00000000000000>>> 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-2.4.1/casa/Utilities/test/tDataType.cc000066400000000000000000000305321321422335000213430ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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(static_cast(0)) == TpOther ); AlwaysAssertExit( whatType(static_cast(0)) == TpBool ); AlwaysAssertExit( whatType(static_cast(0)) == TpChar ); AlwaysAssertExit( whatType(static_cast(0)) == TpUChar ); AlwaysAssertExit( whatType(static_cast(0)) == TpShort ); AlwaysAssertExit( whatType(static_cast(0)) == TpUShort ); AlwaysAssertExit( whatType(static_cast(0)) == TpInt ); AlwaysAssertExit( whatType(static_cast(0)) == TpUInt ); AlwaysAssertExit( whatType(static_cast(0)) == TpInt64 ); AlwaysAssertExit( whatType(static_cast(0)) == TpFloat ); AlwaysAssertExit( whatType(static_cast(0)) == TpDouble ); AlwaysAssertExit( whatType(static_cast(0)) == TpComplex ); AlwaysAssertExit( whatType(static_cast(0)) == TpDComplex ); AlwaysAssertExit( whatType(static_cast(0)) == TpString ); AlwaysAssertExit( whatType(static_cast(0)) == TpTable ); AlwaysAssertExit( whatType(static_cast *>(0)) == TpArrayBool ); AlwaysAssertExit( whatType(static_cast *>(0)) == TpArrayChar ); AlwaysAssertExit( whatType(static_cast *>(0)) == TpArrayUChar ); AlwaysAssertExit( whatType(static_cast *>(0)) == TpArrayShort ); AlwaysAssertExit( whatType(static_cast *>(0)) == TpArrayUShort ); AlwaysAssertExit( whatType(static_cast *>(0)) == TpArrayInt ); AlwaysAssertExit( whatType(static_cast *>(0)) == TpArrayUInt ); AlwaysAssertExit( whatType(static_cast *>(0)) == TpArrayInt64 ); AlwaysAssertExit( whatType(static_cast *>(0)) == TpArrayFloat ); AlwaysAssertExit( whatType(static_cast *>(0)) == TpArrayDouble ); AlwaysAssertExit( whatType(static_cast *>(0)) == TpArrayComplex ); AlwaysAssertExit( whatType(static_cast *>(0)) == TpArrayDComplex ); AlwaysAssertExit( whatType(static_cast *>(0)) == TpArrayString ); AlwaysAssertExit( whatType(static_cast(0)) == TpRecord ); AlwaysAssertExit( whatType(static_cast(0)) == TpQuantity ) ; AlwaysAssertExit( whatType(static_cast *>(0)) == 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 (AipsError x) { hadExcp = True; } AlwaysAssert(hadExcp, AipsError); } void excpAsArray(DataType type) { Bool hadExcp = False; try { asArray(type); } catch (AipsError 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 (AipsError x) { cout << "Caught an exception: " << x.getMesg() << endl; return 1; } cout << "OK" << endl; return 0; } casacore-2.4.1/casa/Utilities/test/tDefaultValue.cc000066400000000000000000000042051321422335000222070ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { cout << "\nCaught an exception: " << x.getMesg() << endl; } cout << "OK" << endl; return 0; } casacore-2.4.1/casa/Utilities/test/tDynBuffer.cc000066400000000000000000000067561321422335000215270ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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-2.4.1/casa/Utilities/test/tFallible.cc000066400000000000000000000044121321422335000213400ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { caught = True; } AlwaysAssertExit(caught); caught = False; try { fi5.value(); } catch (AipsError x) { caught = True; } AlwaysAssertExit(caught); cout << "OK" << endl; return 0; } casacore-2.4.1/casa/Utilities/test/tGenSort.cc000066400000000000000000000221421321422335000212070ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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-2.4.1/casa/Utilities/test/tGenSort.out000066400000000000000000000404101321422335000214270ustar00rootroot00000000000000heapSort 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-2.4.1/casa/Utilities/test/tGenSort.run000066400000000000000000000015041321422335000214250ustar00rootroot00000000000000#!/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-2.4.1/casa/Utilities/test/tLinearSearch.cc000066400000000000000000000140771321422335000221760ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/casa/Utilities/test/tPrecision.cc000066400000000000000000000063471321422335000215720ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# Includes #include #include #include void testit( const Vector& x, const Vector& y, const uInt expectedPrecision ) { 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 (AipsError x) { cout << x.getMesg() << endl; return 1; } return 0; // exit with success status } casacore-2.4.1/casa/Utilities/test/tPtrHolder.cc000066400000000000000000000072211321422335000215320ustar00rootroot00000000000000//# tPtrHolder.cc: Test the tPtrHolder class. //# Copyright (C) 1994,1995,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library is distributed in the hope that it will be useful, but WITHOUT //# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or //# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, 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 //# //# $Id$ //# Includes #include #include #include #include #include // This program should not have any memory leaks. void do_nothing(Int *dummy) { AlwaysAssertExit(dummy == 0); } void do_nothing2(const Int *dummy) { AlwaysAssertExit(dummy == 0); } void tSPtr() { { Int *ip = new Int; SPtrHolder ph(ip); // PtrHolder(T *, Bool=False) AlwaysAssertExit(ph.ptr() == ip); Int *ip2 = ph.transfer(); AlwaysAssertExit(ip2 == ip); AlwaysAssertExit(ph.ptr() == 0); do_nothing (ph.ptr()); do_nothing2 (ph.ptr()); delete ip2; } { // Throw an exception to make sure nothing is leaked Bool isCaught = False; try { Int *ip = new Int; SPtrHolder ph(ip); throw(AipsError("testing...")); } catch (AipsError x) { isCaught = True; } AlwaysAssertExit(isCaught); } } int main() { { PtrHolder ph; // PtrHolder() AlwaysAssertExit(ph.ptr() == 0); // ptr() do_nothing(ph); // operator T*() // Check that the conversion operator will also match const T* // functions. It does under CFront, if it doesn't under some // other compiler it may be necessary to add a constT* conversion // operator to PtrHolder. do_nothing2(ph); } { Int *ip = new Int[1000]; PtrHolder ph(ip, True); // PtrHolder(T *, Bool=True) AlwaysAssertExit(ph.isCArray() == True); // isCarray() AlwaysAssertExit(ph.ptr() == ip); } { Int *ip = new Int; PtrHolder ph(ip); // PtrHolder(T *, Bool=False) AlwaysAssertExit(ph.isCArray() == False); AlwaysAssertExit(ph.ptr() == ip); } { Int *ip = new Int[1000]; PtrHolder ph(ip, True); Int *ip2 = new Int[1000]; ph.set(ip2, True); // set(T*,Bool,Bool=True) delete [] ip2; Int *ip3 = new Int[1000]; // Shouldn't set off a double deletion ph.set(ip3, True, False); // set(T*,Bool,Bool=False) ph.clear(); // clear(Bool=True); AlwaysAssertExit(ph.ptr() == 0); } { // Throw an exception to make sure nothing is leaked Bool isCaught = False; try { Int *ip = new Int[1000]; PtrHolder ph(ip, True); throw(AipsError("testing...")); } catch (AipsError x) { isCaught = True; } AlwaysAssertExit(isCaught); } tSPtr(); cout << "OK" << endl; return 0; } casacore-2.4.1/casa/Utilities/test/tRegex.cc000066400000000000000000000122661321422335000207060ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# Includes #include #include #include #include #include #include #include //# Forward Declarations void a(); void b(); // 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(); b(); } catch (AipsError x) { cout << x.getMesg() << 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("a?bcd(bcdcdd)?"); cout << doMatch (exp, "bdc",3) << " "; cout << doMatch (exp, "abcd",3) << " "; cout << doMatch (exp, "abcd",4) << " "; cout << doMatch (exp, "abcdbcdcdd",10) << endl; cout << doMatch (RXalpha, "bcd",0) << " "; cout << doMatch (RXalpha, "bcd",1) << " "; cout << doMatch (RXalpha, "bcd",2) << " "; cout << doMatch (RXalpha, "bcd",3) << " "; cout << doMatch (RXalpha, "bcd",4) << " "; cout << doMatch (RXalpha, "bcd",100) << endl; cout << String("1").matches(RXdouble) << " "; cout << String("-1").matches(RXdouble) << " "; cout << String("+1").matches(RXdouble) << " "; cout << String("1.").matches(RXdouble) << " "; cout << String("1.1").matches(RXdouble) << " "; cout << String(".1").matches(RXdouble) << " "; cout << String("1.e1").matches(RXdouble) << " "; cout << String("1.1e+1").matches(RXdouble) << " "; cout << String(".1E-1").matches(RXdouble) << " "; cout << String(".1.e-1").matches(RXdouble) << " "; cout << endl; Regex exp2(exp); cout << doMatch(exp2, "abcdbcdcdd",10) << endl; cout << String("abcdbcdcdd").matches(exp2) << " "; cout << String("abcdb").matches(exp2) << " "; cout << String("abcd").matches(exp2) << " "; cout << String("bcd").matches(exp2) << " "; cout << exp2.regexp() << endl; Regex exp5(".+"); cout << doMatch (exp5, "",0) << " "; cout << doMatch (exp5, "",1) << " "; cout << doMatch (exp5, "",2) << " "; cout << doMatch (exp5, "",10) << " "; cout << doMatch (exp5, "a",1) << " "; cout << doMatch (exp5, "a",2) << " "; cout << doMatch (exp5, "\0\0",2) << endl; Vector vec(3); vec(0) = exp; vec(1) = exp5; vec(2) = RXalpha; Vector veci(10); for (Int i=0; i<10; i++) veci(i) = i; cout << vec << endl; cout << veci << endl; exp5 = exp2; cout << doMatch (exp5, "abcdbcdcdd",10) << endl; cout << String("abcdbcdcdd").matches(exp5) << " "; cout << String("abcdb").matches(exp5) << " "; cout << String("abcd").matches(exp5) << " "; cout << String("bcd").matches(exp5) << " "; cout << exp5.regexp() << endl; cout << "end of a" << endl; } // Do some more fancy things. void b() { Regex exp5("a?bcd(bcdcdd)?"); Regex exp2(".+"); cout << exp5.regexp() << " " << exp2.regexp() << endl; Vector vec(3); vec(0) = exp5; vec(1) = exp2; vec(2) = RXalpha; Vector veci(10); for (Int i=0; i<10; i++) veci(i) = i; cout << vec << endl; cout << veci << endl; cout << doMatch (exp5, "abcdbcdcdd",10) << endl; cout << String("abcdbcdcdd").matches(exp5) << " "; cout << String("abcdb").matches(exp5) << " "; cout << String("abcd").matches(exp5) << " "; cout << String("bcd").matches(exp5) << " "; cout << exp5.regexp() << endl; cout << "end of b" << endl; } casacore-2.4.1/casa/Utilities/test/tRegex.out000066400000000000000000000005271321422335000211250ustar00rootroot000000000000004294967295 4294967295 4 10 4294967295 1 2 3 3 3 1 1 0 1 1 1 1 1 1 0 10 1 0 1 1 a?bcd(bcdcdd)? 4294967295 1 2 10 1 2 2 [a?bcd(bcdcdd)?, .+, [A-Za-z]+] [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 10 1 0 1 1 a?bcd(bcdcdd)? end of a a?bcd(bcdcdd)? .+ [a?bcd(bcdcdd)?, .+, [A-Za-z]+] [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 10 1 0 1 1 a?bcd(bcdcdd)? end of b casacore-2.4.1/casa/Utilities/test/tRegex2.cc000066400000000000000000000100201321422335000207520ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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 (AipsError x) { cout << x.getMesg() << 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-2.4.1/casa/Utilities/test/tRegex2.out000066400000000000000000000003651321422335000212070ustar00rootroot00000000000000RTa 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-2.4.1/casa/Utilities/test/tRegex_1.cc000066400000000000000000000231161321422335000211220ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include #include #include #include #include int main () { const Int ntests = 31; 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}"; 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%"); 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-2.4.1/casa/Utilities/test/tRegex_1.out000066400000000000000000000064221321422335000213450ustar00rootroot00000000000000Pattern --> 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-2.4.1/casa/Utilities/test/tRegister.cc000066400000000000000000000075751321422335000214270ustar00rootroot00000000000000//# tRegister.cc: This program tests the Register 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include #include #include #include int main() { foo *ap = 0; bar *bp = 0; foobar *cp = 0; foo2 *dp = 0; bar2 *ep = 0; foo2bar *fp = 0; bar2foo *gp = 0; foobar2 *hp = 0; barfoo2 *ip = 0; foo2bar2 *jp = 0; bar2foo2 *kp = 0; mytmp *t1ap = 0; mytmp *t1bp = 0; mytmp *t1cp = 0; mytmp *t1dp = 0; mytmp2 *t2ap = 0; mytmp2 *t2bp = 0; mytmp2 *t2cp = 0; mytmp2 *t2dp = 0; cout << "foo: " << Register(ap) << endl; cout << "bar: " << Register(bp) << endl; cout << "foobar: " << Register(cp) << endl; cout << "foo2: " << Register(dp) << endl; cout << "bar2: " << Register(ep) << endl; cout << "foo2bar: " << Register(fp) << endl; cout << "bar2foo: " << Register(gp) << endl; cout << "foobar2: " << Register(hp) << endl; cout << "barfoo2: " << Register(ip) << endl; cout << "foo2bar2: " << Register(jp) << endl; cout << "bar2foo2: " << Register(kp) << endl; cout << "-- -- -- -- -- -- -- -- -- -- -- -- -- --" << endl; cout << "foo: " << Register(ap) << endl; cout << "bar: " << Register(bp) << endl; cout << "foobar: " << Register(cp) << endl; cout << "foo2: " << Register(dp) << endl; cout << "bar2: " << Register(ep) << endl; cout << "foo2bar: " << Register(fp) << endl; cout << "bar2foo: " << Register(gp) << endl; cout << "foobar2: " << Register(hp) << endl; cout << "barfoo2: " << Register(ip) << endl; cout << "foo2bar2: " << Register(jp) << endl; cout << "bar2foo2: " << Register(kp) << endl; cout << "-- -- -- -- -- -- -- -- -- -- -- -- -- --" << endl; cout << "mytmp: " << Register(t1ap) << endl; cout << "mytmp: " << Register(t1bp) << endl; cout << "mytmp: " << Register(t1cp) << endl; cout << "mytmp: " << Register(t1dp) << endl; cout << "mytmp2: " << Register(t2ap) << endl; cout << "mytmp2: " << Register(t2bp) << endl; cout << "mytmp2: " << Register(t2cp) << endl; cout << "mytmp2: " << Register(t2dp) << endl; cout << "-- -- -- -- -- -- -- -- -- -- -- -- -- --" << endl; cout << "mytmp: " << Register(t1ap) << endl; cout << "mytmp: " << Register(t1bp) << endl; cout << "mytmp: " << Register(t1cp) << endl; cout << "mytmp: " << Register(t1dp) << endl; cout << "mytmp2: " << Register(t2ap) << endl; cout << "mytmp2: " << Register(t2bp) << endl; cout << "mytmp2: " << Register(t2cp) << endl; cout << "mytmp2: " << Register(t2dp) << endl; return 0; } casacore-2.4.1/casa/Utilities/test/tRegister.h000066400000000000000000000043211321422335000212530ustar00rootroot00000000000000//# : this defines , which ... //# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef CASA_TREGISTER_H #define CASA_TREGISTER_H // // Example classes for test of Register class // // // // // //# Classes you should understand before using this one. //
      • Register // // // Make some classes to test the templated class Register. // class foo {}; class bar {}; class foobar : public foo, public bar {}; class barfoo : public bar, public foo {}; class foo2 : public foo {}; class bar2 : public bar {}; class foo2bar : public foo2, public bar {}; class bar2foo : public bar2, public foo {}; class foobar2 : public foo, public bar2 {}; class barfoo2 : public bar, public foo2 {}; class foo2bar2 : public foo2, public bar2 {}; class bar2foo2 : public bar2, public foo2 {}; template class mytmp : public foo2bar2 {}; template class mytmp2 : public bar2foo2 {}; #endif casacore-2.4.1/casa/Utilities/test/tRegister.out000066400000000000000000000012521321422335000216330ustar00rootroot00000000000000foo: 1 bar: 2 foobar: 3 foo2: 4 bar2: 5 foo2bar: 6 bar2foo: 7 foobar2: 8 barfoo2: 9 foo2bar2: 10 bar2foo2: 11 -- -- -- -- -- -- -- -- -- -- -- -- -- -- foo: 1 bar: 2 foobar: 3 foo2: 4 bar2: 5 foo2bar: 6 bar2foo: 7 foobar2: 8 barfoo2: 9 foo2bar2: 10 bar2foo2: 11 -- -- -- -- -- -- -- -- -- -- -- -- -- -- mytmp: 12 mytmp: 13 mytmp: 14 mytmp: 15 mytmp2: 16 mytmp2: 17 mytmp2: 18 mytmp2: 19 -- -- -- -- -- -- -- -- -- -- -- -- -- -- mytmp: 12 mytmp: 13 mytmp: 14 mytmp: 15 mytmp2: 16 mytmp2: 17 mytmp2: 18 mytmp2: 19 casacore-2.4.1/casa/Utilities/test/tSort.cc000066400000000000000000000203101321422335000205500ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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 #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-2.4.1/casa/Utilities/test/tStringDistance.cc000066400000000000000000000044721321422335000225550ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/casa/Utilities/test/tStringDistance.out000066400000000000000000000043111321422335000227670ustar00rootroot00000000000000abcd 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-2.4.1/casa/Utilities/test/tStringDistance.run000077500000000000000000000013331321422335000227700ustar00rootroot00000000000000#!/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-2.4.1/casa/aips.cc000066400000000000000000000027011321422335000154430ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/casa/aips.h000066400000000000000000000033351321422335000153110ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/casa/aipsdef.h000066400000000000000000000074311321422335000157710ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 // With sgi the AIPS_USE_NEW_SGI switch is always set to cater for // still existing problems in FFTPack and SquareMatrix. It should be removed // at some stage. // Note that for the gcc compiler 'std::' is recognised as '::' for now. #if defined(__sgi) #define AIPS_USE_NEW_SGI #define AIPS_SGI namespace std {}; #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 // SUN Native compiler has trouble with typedef inside class. // PGI compiler (QK_USER) on Cray XT3 needs throw specification in .cc file. #if defined(AIPS_SUN_NATIVE) #define WHATEVER_SUN_TYPEDEF(X) X:: #define WHATEVER_TYPENAME #define WHATEVER_SUN_EXCEPTSPEC(X) throw(X) #else #define WHATEVER_SUN_TYPEDEF(X) #define WHATEVER_TYPENAME typename #if defined(AIPS_CRAY_PGI) || defined(AIPS_GCC) #define WHATEVER_SUN_EXCEPTSPEC(X) throw(X) #else #define WHATEVER_SUN_EXCEPTSPEC(X) #endif #endif #if defined(AIPS_USE_NEW_SGI) || defined(AIPS_GCC) || defined(AIPS_CRAY_PGI) #if defined(WHATEVER_VECTOR_FORWARD_DEC) #undef WHATEVER_VECTOR_FORWARD_DEC #endif #else #define WHATEVER_VECTOR_FORWARD_DEC template class vector #endif #endif casacore-2.4.1/casa/aipsenv.h000066400000000000000000000123201321422335000160140ustar00rootroot00000000000000//# aipsenv.h: Global initialization for special Casacore macros //# Copyright (C) 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ // 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 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 } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/casa/aipstype.h000066400000000000000000000037351321422335000162170ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (UseCasacoreNamespace) # 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-2.4.1/casa/aipsxtype.h000066400000000000000000000034371321422335000164060ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/casa/apps/000077500000000000000000000000001321422335000151435ustar00rootroot00000000000000casacore-2.4.1/casa/apps/CMakeLists.txt000066400000000000000000000004301321422335000177000ustar00rootroot00000000000000# Programs foreach(prog casahdf5support) add_executable (${prog} ${prog}.cc) target_link_libraries (${prog} casa_casa) install(TARGETS ${prog} DESTINATION bin) endforeach() # Scripts foreach(prog countcode) install(PROGRAMS ${prog} DESTINATION bin) endforeach() casacore-2.4.1/casa/apps/casahdf5support.cc000066400000000000000000000032641321422335000205720ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/casa/apps/countcode000077500000000000000000000441471321422335000170660ustar00rootroot00000000000000#!/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. # 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), ('Flex', ['l', 'll'], None, [], '//', '/\*', '\*/', 1), ('Bison', ['y', 'yy'], None, [], '//', '/\*', '\*/', 1), ('Python', ['py', 'python'], 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 '\0' in chunk: return False if len(chunk) < CHUNKSIZE: break finally: fin.close() return True # 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: reComm2a = re.compile('\s*' + scomm + '\s*' + ecomm + '\s*') reComm2b = re.compile('\s*' + scomm + '(.*)' + ecomm + '\s*') reSComm = re.compile(scomm) reEComm = re.compile(ecomm) reTillSComm = re.compile('.*' + scomm + '\s*') reTillEComm = re.compile('.*' + ecomm + '\s*') reFromSComm = re.compile('\s*' + scomm + '.*') reFromEComm = re.compile('\s*' + ecomm + '.*') # 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): 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) 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': 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('-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) 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-2.4.1/casa/apps/plotmemory000077500000000000000000000015061321422335000173020ustar00rootroot00000000000000#!/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-2.4.1/casa/apps/watchmemory000077500000000000000000000043031321422335000174300ustar00rootroot00000000000000#!/usr/bin/env python import time import string import sys import commands 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=commands.getoutput(cmd).split("\n") if len(psoutput)<2: return None elif len(psoutput)>2: 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-2.4.1/casa/casa.dox000066400000000000000000000055151321422335000156310ustar00rootroot00000000000000//# 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: aips2-request@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, so several classes in modules Containers // and Utilities are superseded by their STL counterparts. However, they // are still used in some Casacore code. // Furthermore, some classes offer some extra functionality compared // to STL. } casacore-2.4.1/casa/complex.h000066400000000000000000000035521321422335000160250ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef CASA_STD_COMPLEX_H #define CASA_STD_COMPLEX_H // Make sure any special macros are set #include #if defined(AIPS_SGI) #include #else #include #ifndef AIPS_CRAY_PGI #define NEEDS_LOG10_COMPLEX #endif #endif 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-2.4.1/casa/config.h.in000066400000000000000000000025561321422335000162330ustar00rootroot00000000000000//# 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: aips2-request@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-2.4.1/casa/fstream.h000066400000000000000000000032641321422335000160170ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef CASA_FSTREAM_H #define CASA_FSTREAM_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::fstream; using std::ifstream; using std::ofstream; using std::filebuf; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/casa/iomanip.h000066400000000000000000000033461321422335000160130ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef CASA_IOMANIP_H #define CASA_IOMANIP_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::setw; using std::setfill; using std::setprecision; using std::setbase; using std::resetiosflags; using std::setiosflags; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/casa/iosfwd.h000066400000000000000000000036301321422335000156460ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/casa/iosstrfwd.h000066400000000000000000000034021321422335000163740ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/casa/iostream.h000066400000000000000000000043671321422335000162060ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/casa/istream.h000066400000000000000000000033641321422335000160230ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef CASA_ISTREAM_H #define CASA_ISTREAM_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 //# gcc has no yet. Change later #if defined(__GNUG__) #include #else #include namespace casacore { //# NAMESPACE CASACORE - BEGIN using std::istream; using std::ws; } //# NAMESPACE CASACORE - END #endif #endif casacore-2.4.1/casa/math.h000066400000000000000000000056771321422335000153210ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef CASA_STD_MATH_H #define CASA_STD_MATH_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 #if defined(__APPLE__) || defined(AIPS_DARWIN) #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN using std::isnan; using std::isinf; using std::isfinite; } //# NAMESPACE CASACORE - END #endif #if defined(AIPS_SGI) || defined(AIPS_SUN_NATIVE) # include #else # include # if !defined(AIPS_INTELCC) namespace casacore { //# NAMESPACE CASACORE - BEGIN using std::abs; } //# NAMESPACE CASACORE - END # endif # if !(defined(AIPS_KAICC) || defined(AIPS_GCC) || defined(AIPS_INTELCC) || defined(AIPS_DARWIN) || defined(AIPS_CRAY_PGI)) # define NEEDS_POWFLOATFLOAT # endif #endif 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; //# The following is not yet part of some of the cmath include file. Should be //# removed at some stage # if defined(NEEDS_POWFLOATFLOAT) inline Float pow(Float f1, Float f2) { return Float(pow(Double(f1), Double(f2))); }; # endif //# No float abs defined on the Cray. #if defined(AIPS_CRAY_PGI) inline float abs(float v) { return fabs(v); } #endif } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/casa/namespace.h000066400000000000000000000001741321422335000163070ustar00rootroot00000000000000#ifndef CASACORE_NAMESPACE_H #define CASACORE_NAMESPACE_H #include using namespace casacore; #endif casacore-2.4.1/casa/ostream.h000066400000000000000000000034141321422335000160250ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef CASA_OSTREAM_H #define CASA_OSTREAM_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 //# gcc has no yet. Change later #if defined(__GNUG__) #include #else #include #endif namespace casacore { //# NAMESPACE CASACORE - BEGIN using std::ostream; using std::endl; using std::ends; using std::flush; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/casa/sstream.h000066400000000000000000000033301321422335000160260ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/casa/stdexcept.h000066400000000000000000000032501321422335000163540ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/casa/stdio.h000066400000000000000000000032521321422335000154750ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef CASA_STDIO_H #define CASA_STDIO_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 #if defined(__sgi) || defined(AIPS_SUN_NATIVE) #include #else #include #endif namespace casacore { //# NAMESPACE CASACORE - BEGIN } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/casa/stdlib.h000066400000000000000000000032601321422335000156330ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef CASA_STDLIB_H #define CASA_STDLIB_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 #if defined(__sgi) || defined(AIPS_SUN_NATIVE) #include #else #include #endif namespace casacore { //# NAMESPACE CASACORE - BEGIN } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/casa/stdmap.h000066400000000000000000000031461321422335000156450ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef CASA_STDMAP_H #define CASA_STDMAP_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 using std::map; using std::multimap; using std::pair; using std::allocator; using std::less; #endif casacore-2.4.1/casa/stdvector.h000066400000000000000000000031361321422335000163710ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef CASA_STD_VECTOR_H #define CASA_STD_VECTOR_H //# Define the 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 #if !defined(AIPS_SGI) #include using std::vector; #else #include #endif #endif casacore-2.4.1/casa/string.h000066400000000000000000000032701321422335000156610ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef CASA_STD_STRING_H #define CASA_STD_STRING_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 #if defined(__sgi) || defined(AIPS_SUN_NATIVE) #include #else #include #endif namespace casacore { //# NAMESPACE CASACORE - BEGIN } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/casa/test/000077500000000000000000000000001321422335000151575ustar00rootroot00000000000000casacore-2.4.1/casa/test/CMakeLists.txt000066400000000000000000000004001321422335000177110ustar00rootroot00000000000000set (tests tTypes ) foreach (test ${tests}) add_executable (${test} ${test}.cc) target_link_libraries (${test} casa_casa) add_test (${test} ${CMAKE_SOURCE_DIR}/cmake/cmake_assay ./${test}) add_dependencies(check ${test}) endforeach (test) casacore-2.4.1/casa/test/COPYING000066400000000000000000000430761321422335000162240ustar00rootroot00000000000000 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-2.4.1/casa/test/demo000066400000000000000000000017041321422335000160300ustar00rootroot00000000000000#! /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-2.4.1/casa/test/tTypes.cc000066400000000000000000000035631321422335000167650ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# Includes #include #include int main() { { // Make sure that Char is signed, uChar is unsigned. Char c = 0; c--; Int C = c; AlwaysAssertExit(C == -1); uChar cc = 0; cc--; 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-2.4.1/casa/typeinfo.h000066400000000000000000000031471321422335000162130ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef CASA_TYPEINFO_H #define CASA_TYPEINFO_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 } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/casa/vector.h000066400000000000000000000031361321422335000156560ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef CASA_STD_VECTOR_H #define CASA_STD_VECTOR_H //# Define the 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 #if !defined(AIPS_SGI) #include using std::vector; #else #include #endif #endif casacore-2.4.1/casa/version.cc000066400000000000000000000030371321422335000161770ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/casa/version.h000066400000000000000000000037301321422335000160410ustar00rootroot00000000000000//# 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: aips2-request@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 2 #define CASACORE_MINOR_VERSION 4 #define CASACORE_PATCH_VERSION 0 #define CASACORE_VERSION CASACORE_STRINGIFY(CASACORE_MAJOR_VERSION.CASACORE_MINOR_VERSION.CASACORE_PATCH_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-2.4.1/casacore000077700000000000000000000000001321422335000150472.ustar00rootroot00000000000000casacore-2.4.1/changescripts/000077500000000000000000000000001321422335000161265ustar00rootroot00000000000000casacore-2.4.1/changescripts/dataman.sed000066400000000000000000000156511321422335000202400ustar00rootroot00000000000000s%/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-2.4.1/changescripts/latticemath.sed000066400000000000000000000066141321422335000211310ustar00rootroot00000000000000s%/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-2.4.1/changescripts/lel.sed000066400000000000000000000042571321422335000174070ustar00rootroot00000000000000s%/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-2.4.1/changescripts/msoper.sed000066400000000000000000000023401321422335000201270ustar00rootroot00000000000000s%/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-2.4.1/changescripts/mssel.sed000066400000000000000000000156621321422335000177600ustar00rootroot00000000000000s%/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-2.4.1/changescripts/regions.sed000066400000000000000000000050511321422335000202720ustar00rootroot00000000000000s%/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-2.4.1/changescripts/taql.sed000066400000000000000000000076721321422335000176000ustar00rootroot00000000000000s%/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-2.4.1/changescripts/updateall000077500000000000000000000003731321422335000200320ustar00rootroot00000000000000#!/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-2.4.1/changescripts/updateinclude000077500000000000000000000013711321422335000207040ustar00rootroot00000000000000#!/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-2.4.1/changescripts/updatenamespace000077500000000000000000000004331321422335000212130ustar00rootroot00000000000000#!/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-2.4.1/changescripts/updatepath000077500000000000000000000004301321422335000202100ustar00rootroot00000000000000#!/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-2.4.1/cmake/000077500000000000000000000000001321422335000143515ustar00rootroot00000000000000casacore-2.4.1/cmake/CTest2JUnit.xsl000066400000000000000000000153101321422335000171570ustar00rootroot00000000000000 BuildName: BuildStamp: Name: Generator: CompilerName: OSName: Hostname: OSRelease: OSVersion: OSPlatform: Is64Bits: VendorString: VendorID: FamilyID: ModelID: ProcessorCacheSize: NumberOfLogicalCPU: NumberOfPhysicalCPU: TotalVirtualMemory: TotalPhysicalMemory: LogicalProcessorsPerPhysical: ProcessorClockFrequency: casacore-2.4.1/cmake/FindBISON.cmake000066400000000000000000000146041321422335000170330ustar00rootroot00000000000000# - Find bison executable and provides macros to generate custom build rules # The module defines the following variables: # # BISON_EXECUTABLE - path to the bison program # BISON_VERSION - version of bison # BISON_FOUND - true if the program was found # # If bison is found, the module defines the macros: # BISON_TARGET( [VERBOSE ] # [COMPILE_FLAGS ]) # which will create a custom rule to generate a parser. is # the path to a yacc file. is the name of the source file # generated by bison. A header file is also be generated, and contains # the token list. If COMPILE_FLAGS option is specified, the next # parameter is added in the bison command line. if VERBOSE option is # specified, is created and contains verbose descriptions of the # grammar and parser. The macro defines a set of variables: # BISON_${Name}_DEFINED - true is the macro ran successfully # BISON_${Name}_INPUT - The input source file, an alias for # BISON_${Name}_OUTPUT_SOURCE - The source file generated by bison # BISON_${Name}_OUTPUT_HEADER - The header file generated by bison # BISON_${Name}_OUTPUTS - The sources files generated by bison # BISON_${Name}_COMPILE_FLAGS - Options used in the bison command line # # ==================================================================== # Example: # # find_package(BISON) # BISON_TARGET(MyParser parser.y ${CMAKE_CURRENT_BINARY_DIR}/parser.cpp) # add_executable(Foo main.cpp ${BISON_MyParser_OUTPUTS}) # ==================================================================== #============================================================================= # Copyright 2009 Kitware, Inc. # Copyright 2006 Tristan Carel # # 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.) FIND_PROGRAM(BISON_EXECUTABLE bison DOC "path to the bison executable") MARK_AS_ADVANCED(BISON_EXECUTABLE) IF(BISON_EXECUTABLE) EXECUTE_PROCESS(COMMAND ${BISON_EXECUTABLE} --version OUTPUT_VARIABLE BISON_version_output ERROR_VARIABLE BISON_version_error RESULT_VARIABLE BISON_version_result OUTPUT_STRIP_TRAILING_WHITESPACE) IF(NOT ${BISON_version_result} EQUAL 0) MESSAGE(SEND_ERROR "Command \"${BISON_EXECUTABLE} --version\" failed with output:\n${BISON_version_error}") ELSE() STRING(REGEX REPLACE "^bison \\(GNU Bison\\) ([^\n]+)\n.*" "\\1" BISON_VERSION "${BISON_version_output}") ENDIF() # internal macro MACRO(BISON_TARGET_option_verbose Name BisonOutput filename) LIST(APPEND BISON_TARGET_cmdopt "--verbose") GET_FILENAME_COMPONENT(BISON_TARGET_output_path "${BisonOutput}" PATH) GET_FILENAME_COMPONENT(BISON_TARGET_output_name "${BisonOutput}" NAME_WE) ADD_CUSTOM_COMMAND(OUTPUT ${filename} COMMAND ${CMAKE_COMMAND} ARGS -E copy "${BISON_TARGET_output_path}/${BISON_TARGET_output_name}.output" "${filename}" DEPENDS "${BISON_TARGET_output_path}/${BISON_TARGET_output_name}.output" COMMENT "[BISON][${Name}] Copying bison verbose table to ${filename}" WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) SET(BISON_${Name}_VERBOSE_FILE ${filename}) LIST(APPEND BISON_TARGET_extraoutputs "${BISON_TARGET_output_path}/${BISON_TARGET_output_name}.output") ENDMACRO(BISON_TARGET_option_verbose) # internal macro MACRO(BISON_TARGET_option_extraopts Options) SET(BISON_TARGET_extraopts "${Options}") SEPARATE_ARGUMENTS(BISON_TARGET_extraopts) LIST(APPEND BISON_TARGET_cmdopt ${BISON_TARGET_extraopts}) ENDMACRO(BISON_TARGET_option_extraopts) #============================================================ # BISON_TARGET (public macro) #============================================================ # MACRO(BISON_TARGET Name BisonInput BisonOutput) SET(BISON_TARGET_output_header "") SET(BISON_TARGET_command_opt "") SET(BISON_TARGET_outputs "${BisonOutput}") IF(NOT ${ARGC} EQUAL 3 AND NOT ${ARGC} EQUAL 5 AND NOT ${ARGC} EQUAL 7) MESSAGE(SEND_ERROR "Usage") ELSE() # Parsing parameters IF(${ARGC} GREATER 5 OR ${ARGC} EQUAL 5) IF("${ARGV3}" STREQUAL "VERBOSE") BISON_TARGET_option_verbose(${Name} ${BisonOutput} "${ARGV4}") ENDIF() IF("${ARGV3}" STREQUAL "COMPILE_FLAGS") BISON_TARGET_option_extraopts("${ARGV4}") ENDIF() ENDIF() IF(${ARGC} EQUAL 7) IF("${ARGV5}" STREQUAL "VERBOSE") BISON_TARGET_option_verbose(${Name} ${BisonOutput} "${ARGV6}") ENDIF() IF("${ARGV5}" STREQUAL "COMPILE_FLAGS") BISON_TARGET_option_extraopts("${ARGV6}") ENDIF() ENDIF() # Header's name generated by bison (see option -d) LIST(APPEND BISON_TARGET_cmdopt "-d") STRING(REGEX REPLACE "^(.*)(\\.[^.]*)$" "\\2" _fileext "${ARGV2}") STRING(REPLACE "c" "h" _fileext ${_fileext}) STRING(REGEX REPLACE "^(.*)(\\.[^.]*)$" "\\1${_fileext}" BISON_${Name}_OUTPUT_HEADER "${ARGV2}") LIST(APPEND BISON_TARGET_outputs "${BISON_${Name}_OUTPUT_HEADER}") ADD_CUSTOM_COMMAND(OUTPUT ${BISON_TARGET_outputs} ${BISON_TARGET_extraoutputs} COMMAND ${BISON_EXECUTABLE} ARGS ${BISON_TARGET_cmdopt} -o ${ARGV2} ${ARGV1} DEPENDS ${ARGV1} COMMENT "[BISON][${Name}] Building parser with bison ${BISON_VERSION}" WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) # define target variables SET(BISON_${Name}_DEFINED TRUE) SET(BISON_${Name}_INPUT ${ARGV1}) SET(BISON_${Name}_OUTPUTS ${BISON_TARGET_outputs}) SET(BISON_${Name}_COMPILE_FLAGS ${BISON_TARGET_cmdopt}) SET(BISON_${Name}_OUTPUT_SOURCE "${BisonOutput}") ENDIF(NOT ${ARGC} EQUAL 3 AND NOT ${ARGC} EQUAL 5 AND NOT ${ARGC} EQUAL 7) ENDMACRO(BISON_TARGET) # #============================================================ ENDIF(BISON_EXECUTABLE) INCLUDE(FindPackageHandleStandardArgs) FIND_PACKAGE_HANDLE_STANDARD_ARGS(BISON DEFAULT_MSG BISON_EXECUTABLE) # FindBISON.cmake ends here casacore-2.4.1/cmake/FindCFITSIO.cmake000066400000000000000000000062351321422335000172620ustar00rootroot00000000000000# - 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-2.4.1/cmake/FindDL.cmake000066400000000000000000000010571321422335000164560ustar00rootroot00000000000000# - 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-2.4.1/cmake/FindFFTW3.cmake000066400000000000000000000071521321422335000170120ustar00rootroot00000000000000# - 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-2.4.1/cmake/FindFLEX.cmake000066400000000000000000000124701321422335000167160ustar00rootroot00000000000000# - Find flex executable and provides a macro to generate custom build rules # # The module defines the following variables: # FLEX_FOUND - true is flex executable is found # FLEX_EXECUTABLE - the path to the flex executable # FLEX_VERSION - the version of flex # FLEX_LIBRARIES - The flex libraries # # If flex is found on the system, the module provides the macro: # FLEX_TARGET(Name FlexInput FlexOutput [COMPILE_FLAGS ]) # which creates a custom command to generate the file from # the file. If COMPILE_FLAGS option is specified, the next # parameter is added to the flex command line. Name is an alias used to # get details of this custom command. Indeed the macro defines the # following variables: # FLEX_${Name}_DEFINED - true is the macro ran successfully # FLEX_${Name}_OUTPUTS - the source file generated by the custom rule, an # alias for FlexOutput # FLEX_${Name}_INPUT - the flex source file, an alias for ${FlexInput} # # Flex scanners oftenly use tokens defined by Bison: the code generated # by Flex depends of the header generated by Bison. This module also # defines a macro: # ADD_FLEX_BISON_DEPENDENCY(FlexTarget BisonTarget) # which adds the required dependency between a scanner and a parser # where and are the first parameters of # respectively FLEX_TARGET and BISON_TARGET macros. # # ==================================================================== # Example: # # find_package(BISON) # find_package(FLEX) # # BISON_TARGET(MyParser parser.y ${CMAKE_CURRENT_BINARY_DIR}/parser.cpp # FLEX_TARGET(MyScanner lexer.l ${CMAKE_CURRENT_BIANRY_DIR}/lexer.cpp) # ADD_FLEX_BISON_DEPENDENCY(MyScanner MyParser) # # include_directories(${CMAKE_CURRENT_BINARY_DIR}) # add_executable(Foo # Foo.cc # ${BISON_MyParser_OUTPUTS} # ${FLEX_MyScanner_OUTPUTS} # ) # ==================================================================== #============================================================================= # Copyright 2009 Kitware, Inc. # Copyright 2006 Tristan Carel # # 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.) FIND_PROGRAM(FLEX_EXECUTABLE flex DOC "path to the flex executable") MARK_AS_ADVANCED(FLEX_EXECUTABLE) FIND_LIBRARY(FL_LIBRARY NAMES fl DOC "path to the fl library") MARK_AS_ADVANCED(FL_LIBRARY) SET(FLEX_LIBRARIES ${FL_LIBRARY}) IF(FLEX_EXECUTABLE) EXECUTE_PROCESS(COMMAND ${FLEX_EXECUTABLE} --version OUTPUT_VARIABLE FLEX_version_output ERROR_VARIABLE FLEX_version_error RESULT_VARIABLE FLEX_version_result OUTPUT_STRIP_TRAILING_WHITESPACE) IF(NOT ${FLEX_version_result} EQUAL 0) MESSAGE(SEND_ERROR "Command \"${FLEX_EXECUTABLE} --version\" failed with output:\n${FLEX_version_error}") ELSE() STRING(REGEX REPLACE "^flex (.*)$" "\\1" FLEX_VERSION "${FLEX_version_output}") ENDIF() #============================================================ # FLEX_TARGET (public macro) #============================================================ # MACRO(FLEX_TARGET Name Input Output) SET(FLEX_TARGET_usage "FLEX_TARGET( [COMPILE_FLAGS ]") IF(${ARGC} GREATER 3) IF(${ARGC} EQUAL 5) IF("${ARGV3}" STREQUAL "COMPILE_FLAGS") SET(FLEX_EXECUTABLE_opts "${ARGV4}") SEPARATE_ARGUMENTS(FLEX_EXECUTABLE_opts) ELSE() MESSAGE(SEND_ERROR ${FLEX_TARGET_usage}) ENDIF() ELSE() MESSAGE(SEND_ERROR ${FLEX_TARGET_usage}) ENDIF() ENDIF() ADD_CUSTOM_COMMAND(OUTPUT ${Output} COMMAND ${FLEX_EXECUTABLE} ARGS ${FLEX_EXECUTABLE_opts} -o${Output} ${Input} DEPENDS ${Input} COMMENT "[FLEX][${Name}] Building scanner with flex ${FLEX_VERSION}" WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) SET(FLEX_${Name}_DEFINED TRUE) SET(FLEX_${Name}_OUTPUTS ${Output}) SET(FLEX_${Name}_INPUT ${Input}) SET(FLEX_${Name}_COMPILE_FLAGS ${FLEX_EXECUTABLE_opts}) ENDMACRO(FLEX_TARGET) #============================================================ #============================================================ # ADD_FLEX_BISON_DEPENDENCY (public macro) #============================================================ # MACRO(ADD_FLEX_BISON_DEPENDENCY FlexTarget BisonTarget) IF(NOT FLEX_${FlexTarget}_OUTPUTS) MESSAGE(SEND_ERROR "Flex target `${FlexTarget}' does not exists.") ENDIF() IF(NOT BISON_${BisonTarget}_OUTPUT_HEADER) MESSAGE(SEND_ERROR "Bison target `${BisonTarget}' does not exists.") ENDIF() SET_SOURCE_FILES_PROPERTIES(${FLEX_${FlexTarget}_OUTPUTS} PROPERTIES OBJECT_DEPENDS ${BISON_${BisonTarget}_OUTPUT_HEADER}) ENDMACRO(ADD_FLEX_BISON_DEPENDENCY) #============================================================ ENDIF(FLEX_EXECUTABLE) INCLUDE(FindPackageHandleStandardArgs) FIND_PACKAGE_HANDLE_STANDARD_ARGS(FLEX DEFAULT_MSG FLEX_EXECUTABLE) # FindFLEX.cmake ends here casacore-2.4.1/cmake/FindGSL.cmake000066400000000000000000000106371321422335000166100ustar00rootroot00000000000000# Script found on KDE-edu list # Permission obtained from Jan Woetzel to use under a BSD-style license # (left to OpenCog to determine the specific BSD license, but this file should # be included in CMake proper soon...). # # Look for the header file # Try to find gnu scientific library GSL # See # http://www.gnu.org/software/gsl/ and # http://gnuwin32.sourceforge.net/packages/gsl.htm # # Once run this will define: # # GSL_FOUND = system has GSL lib # # GSL_LIBRARIES = full path to the libraries # on Unix/Linux with additional linker flags from "gsl-config --libs" # # CMAKE_GSL_CXX_FLAGS = Unix compiler flags for GSL, essentially "`gsl-config --cxxflags`" # # GSL_INCLUDE_DIR = where to find headers # # GSL_LINK_DIRECTORIES = link directories, useful for rpath on Unix # GSL_EXE_LINKER_FLAGS = rpath on Unix # # Felix Woelk 07/2004 # Jan Woetzel # # www.mip.informatik.uni-kiel.de # -------------------------------- IF(WIN32) # JW tested with gsl-1.8, Windows XP, MSVS 7.1 SET(GSL_POSSIBLE_ROOT_DIRS ${GSL_ROOT_DIR} $ENV{GSL_ROOT_DIR} ${GSL_DIR} ${GSL_HOME} $ENV{GSL_DIR} $ENV{GSL_HOME} $ENV{EXTRA} ) FIND_PATH(GSL_INCLUDE_DIR NAMES gsl/gsl_cdf.h gsl/gsl_randist.h PATHS ${GSL_POSSIBLE_ROOT_DIRS} PATH_SUFFIXES include DOC "GSL header include dir" ) FIND_LIBRARY(GSL_GSL_LIBRARY NAMES gsl libgsl PATHS ${GSL_POSSIBLE_ROOT_DIRS} PATH_SUFFIXES lib DOC "GSL library dir" ) FIND_LIBRARY(GSL_GSLCBLAS_LIBRARY NAMES gslcblas libgslcblas PATHS ${GSL_POSSIBLE_ROOT_DIRS} PATH_SUFFIXES lib DOC "GSL cblas library dir" ) SET(GSL_LIBRARIES ${GSL_GSL_LIBRARY}) #MESSAGE("DBG\n" # "GSL_GSL_LIBRARY=${GSL_GSL_LIBRARY}\n" # "GSL_GSLCBLAS_LIBRARY=${GSL_GSLCBLAS_LIBRARY}\n" # "GSL_LIBRARIES=${GSL_LIBRARIES}") ELSE(WIN32) IF(UNIX) SET(GSL_CONFIG_PREFER_PATH "$ENV{GSL_DIR}/bin" "$ENV{GSL_DIR}" "$ENV{GSL_HOME}/bin" "$ENV{GSL_HOME}" CACHE STRING "preferred path to GSL (gsl-config)") FIND_PROGRAM(GSL_CONFIG gsl-config ${GSL_CONFIG_PREFER_PATH} /usr/bin/ ) # MESSAGE("DBG GSL_CONFIG ${GSL_CONFIG}") IF (GSL_CONFIG) # set CXXFLAGS to be fed into CXX_FLAGS by the user: SET(GSL_CXX_FLAGS "`${GSL_CONFIG} --cflags`") # set INCLUDE_DIRS to prefix+include EXEC_PROGRAM(${GSL_CONFIG} ARGS --prefix OUTPUT_VARIABLE GSL_PREFIX) SET(GSL_INCLUDE_DIR ${GSL_PREFIX}/include CACHE STRING INTERNAL) # set link libraries and link flags EXEC_PROGRAM(${GSL_CONFIG} ARGS --libs OUTPUT_VARIABLE GSL_LIBRARIES) #SET(GSL_LIBRARIES "`${GSL_CONFIG} --libs`") # extract link dirs for rpath EXEC_PROGRAM(${GSL_CONFIG} ARGS --libs OUTPUT_VARIABLE GSL_CONFIG_LIBS ) # split off the link dirs (for rpath) # use regular expression to match wildcard equivalent "-L*" # with is a space or a semicolon STRING(REGEX MATCHALL "[-][L]([^ ;])+" GSL_LINK_DIRECTORIES_WITH_PREFIX "${GSL_CONFIG_LIBS}" ) # MESSAGE("DBG GSL_LINK_DIRECTORIES_WITH_PREFIX=${GSL_LINK_DIRECTORIES_WITH_PREFIX}") # remove prefix -L because we need the pure directory for LINK_DIRECTORIES IF (GSL_LINK_DIRECTORIES_WITH_PREFIX) STRING(REGEX REPLACE "[-][L]" "" GSL_LINK_DIRECTORIES ${GSL_LINK_DIRECTORIES_WITH_PREFIX} ) ENDIF (GSL_LINK_DIRECTORIES_WITH_PREFIX) SET(GSL_EXE_LINKER_FLAGS "-Wl,-rpath,${GSL_LINK_DIRECTORIES}" CACHE STRING INTERNAL) # MESSAGE("DBG GSL_LINK_DIRECTORIES=${GSL_LINK_DIRECTORIES}") # MESSAGE("DBG GSL_EXE_LINKER_FLAGS=${GSL_EXE_LINKER_FLAGS}") # ADD_DEFINITIONS("-DHAVE_GSL") # SET(GSL_DEFINITIONS "-DHAVE_GSL") MARK_AS_ADVANCED( GSL_CXX_FLAGS GSL_INCLUDE_DIR GSL_LIBRARIES GSL_LINK_DIRECTORIES GSL_DEFINITIONS ) #MESSAGE(STATUS "Using GSL from ${GSL_PREFIX}") ELSE(GSL_CONFIG) MESSAGE("FindGSL.cmake: gsl-config not found. Please set it manually. GSL_CONFIG=${GSL_CONFIG}") ENDIF(GSL_CONFIG) ENDIF(UNIX) ENDIF(WIN32) IF(GSL_LIBRARIES) IF(GSL_INCLUDE_DIR OR GSL_CXX_FLAGS) SET(GSL_FOUND 1) ENDIF(GSL_INCLUDE_DIR OR GSL_CXX_FLAGS) ELSE(GSL_LIBRARIES) IF (GSL_FIND_REQUIRED) message(SEND_ERROR "FindGSL.cmake: Unable to find the required GSL libraries") ENDIF(GSL_FIND_REQUIRED) ENDIF(GSL_LIBRARIES) casacore-2.4.1/cmake/FindHDF5.cmake000066400000000000000000000322331321422335000166450ustar00rootroot00000000000000# - Find HDF5, a library for reading and writing self describing array data. # # This module invokes the HDF5 wrapper compiler that should be installed # alongside HDF5. Depending upon the HDF5 Configuration, the wrapper compiler # is called either h5cc or h5pcc. If this succeeds, the module will then call # the compiler with the -show argument to see what flags are used when compiling # an HDF5 client application. # # The module will optionally accept the COMPONENTS argument. If no COMPONENTS # are specified, then the find module will default to finding only the HDF5 C # library. If one or more COMPONENTS are specified, the module will attempt to # find the language bindings for the specified components. Currently, the only # valid components are C and CXX. The module does not yet support finding the # Fortran bindings. If the COMPONENTS argument is not given, the module will # attempt to find only the C bindings. # # On UNIX systems, this module will read the variable HDF5_USE_STATIC_LIBRARIES # to determine whether or not to prefer a static link to a dynamic link for HDF5 # and all of it's dependencies. To use this feature, make sure that the # HDF5_USE_STATIC_LIBRARIES variable is set before the call to find_package. # # To provide the module with a hint about where to find your HDF5 installation, # you can set the environment variable HDF5_ROOT. The Find module will then # look in this path when searching for HDF5 executables, paths, and libraries. # # In addition to finding the includes and libraries required to compile an HDF5 # client application, this module also makes an effort to find tools that come # with the HDF5 distribution that may be useful for regression testing. # # This module will define the following variables: # HDF5_INCLUDE_DIRS - Location of the hdf5 includes # HDF5_DEFINITIONS - Required compiler definitions for HDF5 # HDF5_C_LIBRARIES - Required libraries for the HDF5 C bindings. # HDF5_CXX_LIBRARIES - Required libraries for the HDF5 C++ bindings # HDF5_LIBRARIES - Required libraries for all requested bindings # HDF5_FOUND - true if HDF5 was found on the system # HDF5_LIBRARY_DIRS - the full set of library directories # HDF5_IS_PARALLEL - Whether or not HDF5 was found with parallel IO support # HDF5_C_COMPILER_EXECUTABLE - the path to the HDF5 C wrapper compiler # HDF5_CXX_COMPILER_EXECUTABLE - the path to the HDF5 C++ wrapper compiler # HDF5_DIFF_EXECUTABLE - the path to the HDF5 dataset comparison tool #============================================================================= # Copyright 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 module is maintained by Will Dicharry . include(SelectLibraryConfigurations) include(FindPackageHandleStandardArgs) # List of the valid HDF5 components set( HDF5_VALID_COMPONENTS C CXX ) # try to find the HDF5 wrapper compilers find_program( HDF5_C_COMPILER_EXECUTABLE NAMES h5cc h5pcc HINTS ENV HDF5_ROOT PATH_SUFFIXES bin Bin DOC "HDF5 Wrapper compiler. Used only to detect HDF5 compile flags." ) mark_as_advanced( HDF5_C_COMPILER_EXECUTABLE ) find_program( HDF5_CXX_COMPILER_EXECUTABLE NAMES h5c++ h5pc++ HINTS ENV HDF5_ROOT PATH_SUFFIXES bin Bin DOC "HDF5 C++ Wrapper compiler. Used only to detect HDF5 compile flags." ) mark_as_advanced( HDF5_CXX_COMPILER_EXECUTABLE ) find_program( HDF5_DIFF_EXECUTABLE NAMES h5diff HINTS ENV HDF5_ROOT PATH_SUFFIXES bin Bin DOC "HDF5 file differencing tool." ) mark_as_advanced( HDF5_DIFF_EXECUTABLE ) # Invoke the HDF5 wrapper compiler. The compiler return value is stored to the # return_value argument, the text output is stored to the output variable. macro( _HDF5_invoke_compiler language output return_value ) if( HDF5_${language}_COMPILER_EXECUTABLE ) exec_program( ${HDF5_${language}_COMPILER_EXECUTABLE} ARGS -show OUTPUT_VARIABLE ${output} RETURN_VALUE ${return_value} ) if( ${${return_value}} EQUAL 0 ) # do nothing else() message( STATUS "Unable to determine HDF5 ${language} flags from HDF5 wrapper." ) endif() endif() endmacro() # Parse a compile line for definitions, includes, library paths, and libraries. macro( _HDF5_parse_compile_line compile_line include_paths definitions library_paths libraries ) # Match the include paths string( REGEX MATCHALL "-I([^\" ]+)" include_path_flags "${compile_line}" ) foreach( IPATH ${include_path_flags} ) string( REGEX REPLACE "^-I" "" IPATH ${IPATH} ) string( REGEX REPLACE "//" "/" IPATH ${IPATH} ) list( APPEND ${include_paths} ${IPATH} ) endforeach() # Match the definitions string( REGEX MATCHALL "-D[^ ]*" definition_flags "${compile_line}" ) foreach( DEF ${definition_flags} ) list( APPEND ${definitions} ${DEF} ) endforeach() # Match the library paths string( REGEX MATCHALL "-L([^\" ]+|\"[^\"]+\")" library_path_flags "${compile_line}" ) foreach( LPATH ${library_path_flags} ) string( REGEX REPLACE "^-L" "" LPATH ${LPATH} ) string( REGEX REPLACE "//" "/" LPATH ${LPATH} ) list( APPEND ${library_paths} ${LPATH} ) endforeach() # now search for the library names specified in the compile line (match -l...) # match only -l's preceded by a space or comma # this is to exclude directory names like xxx-linux/ string( REGEX MATCHALL "[, ]-l([^\", ]+)" library_name_flags "${compile_line}" ) # strip the -l from all of the library flags and add to the search list foreach( LIB ${library_name_flags} ) string( REGEX REPLACE "^[, ]-l" "" LIB ${LIB} ) list( APPEND ${libraries} ${LIB} ) endforeach() endmacro() if( HDF5_INCLUDE_DIRS AND HDF5_LIBRARIES ) # Do nothing: we already have HDF5_INCLUDE_PATH and HDF5_LIBRARIES in the # cache, it would be a shame to override them else() _HDF5_invoke_compiler( C HDF5_C_COMPILE_LINE HDF5_C_RETURN_VALUE ) _HDF5_invoke_compiler( CXX HDF5_CXX_COMPILE_LINE HDF5_CXX_RETURN_VALUE ) if( NOT HDF5_FIND_COMPONENTS ) set( HDF5_LANGUAGE_BINDINGS "C" ) else() # add the extra specified components, ensuring that they are valid. foreach( component ${HDF5_FIND_COMPONENTS} ) list( FIND HDF5_VALID_COMPONENTS ${component} component_location ) if( ${component_location} EQUAL -1 ) message( FATAL_ERROR "\"${component}\" is not a valid HDF5 component." ) else() list( APPEND HDF5_LANGUAGE_BINDINGS ${component} ) endif() endforeach() endif() # seed the initial lists of libraries to find with items we know we need set( HDF5_C_LIBRARY_NAMES_INIT hdf5_hl hdf5 ) set( HDF5_CXX_LIBRARY_NAMES_INIT hdf5_cpp ${HDF5_C_LIBRARY_NAMES_INIT} ) foreach( LANGUAGE ${HDF5_LANGUAGE_BINDINGS} ) if( HDF5_${LANGUAGE}_COMPILE_LINE ) _HDF5_parse_compile_line( ${HDF5_${LANGUAGE}_COMPILE_LINE} HDF5_${LANGUAGE}_INCLUDE_FLAGS HDF5_${LANGUAGE}_DEFINITIONS HDF5_${LANGUAGE}_LIBRARY_DIRS HDF5_${LANGUAGE}_LIBRARY_NAMES ) # take a guess that the includes may be in the 'include' sibling directory # of a library directory. foreach( dir ${HDF5_${LANGUAGE}_LIBRARY_DIRS} ) list( APPEND HDF5_${LANGUAGE}_INCLUDE_FLAGS ${dir}/../include ) endforeach() endif() # set the definitions for the language bindings. list( APPEND HDF5_DEFINITIONS ${HDF5_${LANGUAGE}_DEFINITIONS} ) # find the HDF5 include directories find_path( HDF5_${LANGUAGE}_INCLUDE_DIR hdf5.h HINTS ${HDF5_${LANGUAGE}_INCLUDE_FLAGS} ENV HDF5_ROOT HINTS $ENV{HOME}/.local/include PATH_SUFFIXES include Include ) mark_as_advanced( HDF5_${LANGUAGE}_INCLUDE_DIR ) list( APPEND HDF5_INCLUDE_DIRS ${HDF5_${LANGUAGE}_INCLUDE_DIR} ) set( HDF5_${LANGUAGE}_LIBRARY_NAMES ${HDF5_${LANGUAGE}_LIBRARY_NAMES_INIT} ${HDF5_${LANGUAGE}_LIBRARY_NAMES} ) # find the HDF5 libraries foreach( LIB ${HDF5_${LANGUAGE}_LIBRARY_NAMES} ) if( UNIX AND HDF5_USE_STATIC_LIBRARIES ) # According to bug 1643 on the CMake bug tracker, this is the # preferred method for searching for a static library. # See http://www.cmake.org/Bug/view.php?id=1643. We search # first for the full static library name, but fall back to a # generic search on the name if the static search fails. set( THIS_LIBRARY_SEARCH_DEBUG lib${LIB}d.a ${LIB}d ) set( THIS_LIBRARY_SEARCH_RELEASE lib${LIB}.a ${LIB} ) else() set( THIS_LIBRARY_SEARCH_DEBUG ${LIB}d ) set( THIS_LIBRARY_SEARCH_RELEASE ${LIB} ) endif() find_library( HDF5_${LIB}_LIBRARY_DEBUG NAMES ${THIS_LIBRARY_SEARCH_DEBUG} HINTS ${HDF5_${LANGUAGE}_LIBRARY_DIRS} ENV HDF5_ROOT PATH_SUFFIXES lib Lib ) find_library( HDF5_${LIB}_LIBRARY_RELEASE NAMES ${THIS_LIBRARY_SEARCH_RELEASE} HINTS ${HDF5_${LANGUAGE}_LIBRARY_DIRS} ENV HDF5_ROOT PATH_SUFFIXES lib Lib ) select_library_configurations( HDF5_${LIB} ) # even though we adjusted the individual library names in # select_library_configurations, we still need to distinguish # between debug and release variants because HDF5_LIBRARIES will # need to specify different lists for debug and optimized builds. # We can't just use the HDF5_${LIB}_LIBRARY variable (which was set # up by the selection macro above) because it may specify debug and # optimized variants for a particular library, but a list of # libraries is allowed to specify debug and optimized only once. list( APPEND HDF5_${LANGUAGE}_LIBRARIES_DEBUG ${HDF5_${LIB}_LIBRARY_DEBUG} ) list( APPEND HDF5_${LANGUAGE}_LIBRARIES_RELEASE ${HDF5_${LIB}_LIBRARY_RELEASE} ) endforeach() list( APPEND HDF5_LIBRARY_DIRS ${HDF5_${LANGUAGE}_LIBRARY_DIRS} ) # Append the libraries for this language binding to the list of all # required libraries. list( APPEND HDF5_LIBRARIES_DEBUG ${HDF5_${LANGUAGE}_LIBRARIES_DEBUG} ) list( APPEND HDF5_LIBRARIES_RELEASE ${HDF5_${LANGUAGE}_LIBRARIES_RELEASE} ) endforeach() # We may have picked up some duplicates in various lists during the above # process for the language bindings (both the C and C++ bindings depend on # libz for example). Remove the duplicates. if( HDF5_INCLUDE_DIRS ) list( REMOVE_DUPLICATES HDF5_INCLUDE_DIRS ) endif() if( HDF5_LIBRARIES_DEBUG ) list( REMOVE_DUPLICATES HDF5_LIBRARIES_DEBUG ) endif() if( HDF5_LIBRARIES_RELEASE ) list( REMOVE_DUPLICATES HDF5_LIBRARIES_RELEASE ) endif() if( HDF5_LIBRARY_DIRS ) list( REMOVE_DUPLICATES HDF5_LIBRARY_DIRS ) endif() # Construct the complete list of HDF5 libraries with debug and optimized # variants when the generator supports them. if( CMAKE_CONFIGURATION_TYPES OR CMAKE_BUILD_TYPE ) set( HDF5_LIBRARIES debug ${HDF5_LIBRARIES_DEBUG} optimized ${HDF5_LIBRARIES_RELEASE} ) else() set( HDF5_LIBRARIES ${HDF5_LIBRARIES_RELEASE} ) endif() # If the HDF5 include directory was found, open H5pubconf.h to determine if # HDF5 was compiled with parallel IO support set( HDF5_IS_PARALLEL FALSE ) foreach( _dir HDF5_INCLUDE_DIRS ) if( EXISTS "${_dir}/h5pubconf.h" ) file( STRINGS "${_dir}/H5pubconf.h" HDF5_HAVE_PARALLEL_DEFINE REGEX "HAVE_PARALLEL 1" ) if( HDF5_HAVE_PARALLEL_DEFINE ) set( HDF5_IS_PARALLEL TRUE ) endif() endif() endforeach() set( HDF5_IS_PARALLEL ${HDF5_IS_PARALLEL} CACHE BOOL "HDF5 library compiled with parallel IO support" ) mark_as_advanced( HDF5_IS_PARALLEL ) endif() find_package_handle_standard_args( HDF5 DEFAULT_MSG HDF5_LIBRARIES HDF5_INCLUDE_DIRS ) mark_as_advanced( HDF5_INCLUDE_DIRS HDF5_LIBRARIES HDF5_DEFINTIONS HDF5_LIBRARY_DIRS HDF5_C_COMPILER_EXECUTABLE HDF5_CXX_COMPILER_EXECUTABLE ) casacore-2.4.1/cmake/FindNUMPY.cmake000066400000000000000000000105521321422335000170670ustar00rootroot00000000000000# - 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-2.4.1/cmake/FindPthreads.cmake000066400000000000000000000055541321422335000177370ustar00rootroot00000000000000# 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-2.4.1/cmake/FindPython.cmake000066400000000000000000000046511321422335000174430ustar00rootroot00000000000000# - Try to find the Python interpreter, Python header files and libraries. # This macro effectively wraps the FindPythonInterp and FindPythonLibs macros # provided by CMake. # # In addition to the variables that are set by FindPythonInterp and # FindPythonLibs, this will define: # PYTHON_FOUND - system has Python interpreter, Python headers # files and libraries # PYTHON_INCLUDE_DIRS - path to the Python header files # PYTHON_BUILD_DIR - build directory for Python extensions (cached) # PYTHON_INSTALL_DIR - installation directory for Python extensions (cached) # 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: FindPython.cmake 22948 2012-11-23 08:54:47Z loose $ # Set options string to pass to the find_package() commands below. set(_options ${Python_FIND_VERSION}) if(Python_FIND_VERSION_EXACT) list(APPEND _options EXACT) endif(Python_FIND_VERSION_EXACT) if(Python_FIND_QUIETLY) list(APPEND _options QUIET) endif(Python_FIND_QUIETLY) if(Python_FIND_REQUIRED) list(APPEND _options REQUIRED) endif(Python_FIND_REQUIRED) # Search for the Python interpreter. find_package(PythonInterp ${_options}) # Search for the Python header files and libraries. find_package(PythonLibs ${_options}) # Set PYTHON_INCLUDE_DIRS variable, because FindPythonLibs does not do it. set(PYTHON_INCLUDE_DIRS "${PYTHON_INCLUDE_DIR}") # PythonInstall sets PYTHON_BUILD_DIR and PYTHON_INSTALL_DIR #include(PythonInstall) # Set PYTHON_FOUND to TRUE if both Python interpreter and libraries are found. set(PYTHON_FOUND FALSE) if(PYTHONINTERP_FOUND AND PYTHONLIBS_FOUND) set(PYTHON_FOUND TRUE) endif(PYTHONINTERP_FOUND AND PYTHONLIBS_FOUND) casacore-2.4.1/cmake/FindReadline.cmake000066400000000000000000000042001321422335000176730ustar00rootroot00000000000000# - 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-2.4.1/cmake/FindSOFA.cmake000066400000000000000000000012421321422335000167030ustar00rootroot00000000000000# - 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-2.4.1/cmake/FindWCSLIB.cmake000066400000000000000000000055451321422335000171500ustar00rootroot00000000000000# - 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-2.4.1/cmake/PythonInstall.cmake000066400000000000000000000103131321422335000201610ustar00rootroot00000000000000# - 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-2.4.1/cmake/SelectLibraryConfigurations.cmake000066400000000000000000000077321321422335000230430ustar00rootroot00000000000000# 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-2.4.1/cmake/cmake_assay000077500000000000000000000022721321422335000165620ustar00rootroot00000000000000#!/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-2.4.1/coordinates/000077500000000000000000000000001321422335000156035ustar00rootroot00000000000000casacore-2.4.1/coordinates/CMakeLists.txt000066400000000000000000000030661321422335000203500ustar00rootroot00000000000000# # 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 ) target_link_libraries (casa_coordinates casa_fits ${WCSLIB_LIBRARIES} ${CASACORE_ARCH_LIBS}) install (TARGETS casa_coordinates 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 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 Coordinates.h DESTINATION include/casacore/coordinates ) add_subdirectory (Coordinates/test ${EXCL_ALL}) casacore-2.4.1/coordinates/Coordinates.h000066400000000000000000000323201321422335000202260ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/coordinates/Coordinates/000077500000000000000000000000001321422335000200555ustar00rootroot00000000000000casacore-2.4.1/coordinates/Coordinates/Coordinate.cc000066400000000000000000001100021321422335000224450ustar00rootroot00000000000000//#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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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); // Block imgCrd(nAxes); double phi; double theta=0; // initialize, because wcslib not always sets theta // Convert from pixel to world with wcs units int stat; int 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); // Block imgCrd(nAxes); double phi; double theta; // Convert with world with wcs units to pixel int stat; int 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 #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template class Quantum; template class Matrix; 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; 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; // // Call wcsset on the wcs structure void set_wcs (wcsprm& wcs); // 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-2.4.1/coordinates/Coordinates/CoordinateSystem.cc000066400000000000000000004366121321422335000236740ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id: CoordinateSystem.cc 21521 2014-12-10 08:06:42Z gervandiepen $ #include #include #include #include #include #include #include #include #include #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"; 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])); 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])); 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); dummy.toBlock(*(retval->world_maps_p[i])); name = String("worldreplace") + num; subrec.get(name, *(retval->world_replacement_values_p[i])); name = String("pixelmap") + num; subrec.get(name, dummy); dummy.toBlock(*(retval->pixel_maps_p[i])); 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) << 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() { ScopedMutexLock 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-2.4.1/coordinates/Coordinates/CoordinateSystem.h000066400000000000000000001344341321422335000235330ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #ifndef COORDINATES_COORDINATESYSTEM_H #define COORDINATES_COORDINATESYSTEM_H #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template class Matrix; 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 Mutex _mapInitMutex; static 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 (AipsError 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-2.4.1/coordinates/Coordinates/CoordinateUtil.h000066400000000000000000000632011321422335000231550ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/coordinates/Coordinates/Direction2Coordinate.cc000066400000000000000000000071461321422335000244060ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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-2.4.1/coordinates/Coordinates/DirectionCoordinate.cc000066400000000000000000001760341321422335000243270ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #include #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 { DirectionCoordinate myClone; if (conversionType_p == type_p) { myClone = *this; } else { myClone = DirectionCoordinate(*this); myClone.setReferenceConversion(type_p); } Vector units = myClone.worldAxisUnits(); Vector x = myClone.referenceValue(); Vector myRefVal(2); myRefVal[0] = Quantity(x[0], units[0]); myRefVal[1] = Quantity(x[1], units[1]); MDirection myRefDir( myRefVal[0], myRefVal[1], type_p ); x = increment(); Vector inc(2); inc[0] = Quantity(x[0], units[0]); inc[1] = Quantity(x[1], units[1]); Vector myRefPix = myClone.referencePixel(); // get the angle for the linear transformation matrix. Need two world coordinate points. Vector pixVal2 = myRefPix.copy(); pixVal2[0] = myRefPix[0] + 1; // Figure out this coordinate's rotation angle wrt the cardinal directions. // Normally, its 180 degrees (longitude decreases to the right) myClone.toWorld(x, pixVal2); Vector origWorldVal2(2); origWorldVal2[0] = Quantity(x[0], units[0]); origWorldVal2[1] = Quantity(x[1], units[1]); Quantity diffx = origWorldVal2[0] - myRefVal[0]; Double diffXVal = _longitudeDifference( diffx, myRefVal[1], inc[0] ); Quantity diffy = origWorldVal2[1] - myRefVal[1]; Double diffYVal = diffy.getValue("arcsec"); Double hypVal = sqrt(diffXVal*diffXVal + diffYVal*diffYVal); Double origAngle = (fabs(diffYVal/inc[1].getValue("arcsec")) < 1e-8) ? 0 : asin(diffYVal/hypVal); MDirection origWorldDir2( origWorldVal2[0], origWorldVal2[1], type_p ); // newRefDir is in the the requested directionType frame Quantum > newRefDir = MDirection::Convert( myRefDir, directionType )().getAngle(); Vector newRefDirVec(2); newRefDirVec[0] = Quantity(newRefDir.getValue(units[0])[0], units[0]); newRefDirVec[1] = Quantity(newRefDir.getValue(units[1])[1], units[1]); Quantum > newWorld2Dir = MDirection::Convert( origWorldDir2, directionType )().getAngle(); Vector newWorld2Vec(2); newWorld2Vec[0] = Quantity(newWorld2Dir.getValue(units[0])[0], units[0]); newWorld2Vec[1] = Quantity(newWorld2Dir.getValue(units[1])[1], units[1]); // correct for cos(latitude) diffx = (newWorld2Vec[0] - newRefDirVec[0]); diffXVal = _longitudeDifference( diffx, newRefDirVec[1], inc[0] ); //diffXVal = diffx.getValue("arcsec")*cos(newRefDir.getValue("rad")[1]); diffy = newWorld2Vec[1] - newRefDirVec[1]; diffYVal = diffy.getValue("arcsec"); hypVal = sqrt(diffXVal*diffXVal+diffYVal*diffYVal); Double newAngle = (fabs(diffYVal/inc[1].getValue("arcsec")) < 1e-8) ? 0 : asin(diffYVal/hypVal); angle = Quantity(newAngle - origAngle, "rad"); Matrix xform(2, 2, 0); xform.diagonal() = 1.0; DirectionCoordinate converted( directionType, projection_p, newRefDirVec[0], newRefDirVec[1], inc[0], inc[1], xform, myRefPix[0], myRefPix[1] ); return converted; } Double DirectionCoordinate::_longitudeDifference( const Quantity& longAngleDifference, const Quantity& latitude, const Quantity& longitudePixelIncrement ) { Double latInRad = latitude.getValue("rad"); Double diffXVal = longAngleDifference.getValue("arcsec")*cos(latInRad); Double incXVal = longitudePixelIncrement.getValue("arcsec"); if ((abs(diffXVal) - abs(incXVal))/abs(incXVal) > 1 + 1e-6) { // There may be a 2*pi ambiguity somewhere diffXVal = (longAngleDifference - Quantity(360, "deg")).getValue("arcsec")*cos(latInRad); if ((abs(diffXVal) - abs(incXVal))/abs(incXVal) > 1 + 1e-6) { diffXVal = (longAngleDifference + Quantity(360, "deg")).getValue("arcsec")*cos(latInRad); if ((abs(diffXVal) - abs(incXVal))/abs(incXVal) > 1 + 1e-6) { throw AipsError("DirectionCoordinate::_longitudeDifference: Cannot determine diffx"); } } } return diffXVal; } 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 / C::_2pi; 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()); Vector units(worldAxisUnits()); Vector unitsCanon(worldAxisUnits()); // 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()); 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; LinearXform* pLinearF = linear.fourierInvert(errMsg, axes, crpix, scale); if (pLinearF==0) { set_error (errMsg); return 0; } // LinearCoordinate* pLinear = new LinearCoordinate(namesOut, unitsOut, crval, pLinearF->cdelt(), pLinearF->pc(), pLinearF->crpix()); delete pLinearF; return pLinear; } 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; int iret = wcsini(1, 2, &wcs); if (iret != 0) { String errmsg = "wcs wcsini_error: "; errmsg += wcsini_errmsg[iret]; throw(AipsError(errmsg)); } // 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); } int err = wcscopy (1, &(other.wcs_p), &wcs_p); if (err != 0) { String errmsg = "wcs wcscopy_error: "; errmsg += wcscopy_errmsg[err]; throw(AipsError(errmsg)); } 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-2.4.1/coordinates/Coordinates/DirectionCoordinate.h000066400000000000000000001020331321422335000241550ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id: #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; 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 cardinal directions (left-right, up-down). // 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 for which require extra information such as epoch and // position are not supported. The angle parameter is the angle // through which this coordinate had to be rotated clockwise to produce // the new coordinate. 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; static Double _longitudeDifference(const Quantity& longAngleDifference, const Quantity& latitude, const Quantity& longitudePixelIncrement); }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/coordinates/Coordinates/FITSCoordinateUtil.cc000066400000000000000000001667541321422335000240220ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id: #include #include #include #include #include #include #include #include #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 = C::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; } Bool FITSCoordinateUtil::addDirectionCoordinate (CoordinateSystem& cSys, Vector& dirAxes, const ::wcsprm& wcs, LogIO& os) const { // Extract wcs structure pertaining to Direction Coordinate int alloc = 1; // Allocate memory for output structures int nsub = 2; Block axes(nsub); axes[0] = WCSSUB_LONGITUDE; axes[1] = WCSSUB_LATITUDE; // ::wcsprm wcsDest; wcsInit (wcsDest); int ierr = wcssub (alloc, &wcs, &nsub, axes.storage(), &wcsDest); // Bool ok = True; String errMsg; if (ierr!=0) { errMsg = String("wcslib wcssub error: ") + wcssub_errmsg[ierr]; os << LogIO::WARN << errMsg << LogIO::POST; ok = False; } // 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; 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 (AipsError x) { os << LogIO::WARN << x.getMesg() << 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 alloc = 1; // Allocate memory for output structures int nsub = 1; Block axes(wcs.naxis); axes[0] = -(WCSSUB_LONGITUDE | WCSSUB_LATITUDE | WCSSUB_SPECTRAL | WCSSUB_STOKES); // ::wcsprm wcsDest; wcsInit (wcsDest); int ierr = wcssub (alloc, &wcs, &nsub, axes.storage(), &wcsDest); // Bool ok = True; String errMsg; if (ierr!=0) { errMsg = String("wcslib wcssub error: ") + wcssub_errmsg[ierr]; os << LogIO::WARN << errMsg << LogIO::POST; ok = False; } // 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 (AipsError x) { os << LogIO::WARN << x.getMesg() << 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); int alloc = 1; int ierr = wcssub (alloc, &wcs, &nsub, axes.storage(), &wcsDest); // Bool ok = True; String errMsg; if (ierr!=0) { errMsg = String("wcslib wcssub error: ") + wcssub_errmsg[ierr]; os << LogIO::WARN << errMsg << LogIO::POST; ok = False; } // 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); int alloc = 1; int ierr = wcssub (alloc, &wcs, &nsub, axes.storage(), &wcsDest); uInt nc = 1; if(axes[0]-1<(Int)shape.nelements()){ nc = shape(axes[0]-1); // the number of channels of the spectral axis } Bool ok = True; String errMsg; if (ierr!=0) { errMsg = String("wcslib wcssub error: ") + wcssub_errmsg[ierr]; os << LogIO::WARN << errMsg << LogIO::POST; ok = False; } SpectralCoordinate::SpecType nativeSType = SpectralCoordinate::FREQ; // See if we found the axis if (ok && nsub==1) { // 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 (AipsError x) { os << LogIO::WARN << x.getMesg() << 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 (AipsError x) { os << LogIO::WARN << x.getMesg() << 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 (AipsError x) { errMsg = x.getMesg(); 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 { if (int iret = wcsset(&wcs)) { String errmsg = "wcs wcsset_error: "; errmsg += wcsset_errmsg[iret]; throw(AipsError(errmsg)); } } 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)*C::pi/180.0); pc(rotationAxis-1,rotationAxis)= -sin(crota(rotationAxis)*C::pi/180.0); pc(rotationAxis,rotationAxis-1)= sin(crota(rotationAxis)*C::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)*C::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)*C::pi/180.0); pc(rotationAxis+1,rotationAxis)= sin(crota(rotationAxis)*C::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-2.4.1/coordinates/Coordinates/FrequencyAligner.h000066400000000000000000000215231321422335000234740ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/coordinates/Coordinates/FrequencyAligner.tcc000066400000000000000000000316021321422335000240150ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/coordinates/Coordinates/GaussianConvert.cc000066400000000000000000000310061321422335000234770ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# $Id$ //# #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, C::pi); if (pa2 < 0.0) pa2 += C::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-2.4.1/coordinates/Coordinates/GaussianConvert.h000066400000000000000000000122571321422335000233500ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/coordinates/Coordinates/LinearCoordinate.cc000066400000000000000000000524611321422335000236160ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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; int iret = wcsini(1, naxis, &wcs); if (iret != 0) { String errmsg = "wcs wcsini_error: "; errmsg += wcsini_errmsg[iret]; throw(AipsError(errmsg)); } // 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-2.4.1/coordinates/Coordinates/LinearXform.cc000066400000000000000000000222211321422335000226110ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ //#--------------------------------------------------------------------------- #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-2.4.1/coordinates/Coordinates/LinearXform.h000066400000000000000000000154061321422335000224620ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #ifndef COORDINATES_LINEARXFORM_H #define COORDINATES_LINEARXFORM_H #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template class Vector; template class Matrix; 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-2.4.1/coordinates/Coordinates/LinearXform2.cc000066400000000000000000000057241321422335000227040ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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 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; if (MeasTable::Observatory (pos, telescope)) { 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().getRefPtr()->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"))*C::pi/180.0, (subRec2.asDouble("value"))*C::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-2.4.1/coordinates/Coordinates/ObsInfo.h000066400000000000000000000212101321422335000215610ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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-2.4.1/coordinates/Coordinates/Projection.cc000066400000000000000000000242631321422335000225070ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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-2.4.1/coordinates/Coordinates/Projection.h000066400000000000000000000155771321422335000223610ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #ifndef COORDINATES_PROJECTION_H #define COORDINATES_PROJECTION_H #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-2.4.1/coordinates/Coordinates/QualityCoordinate.cc000066400000000000000000000363341321422335000240350ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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); } 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-2.4.1/coordinates/Coordinates/QualityCoordinate.h000066400000000000000000000236731321422335000237010ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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-2.4.1/coordinates/Coordinates/Spectral2Coordinate.cc000066400000000000000000000317071321422335000242430ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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; int err = wcscopy (1, &(wcs), &wcs_p); if (err != 0) { String errmsg = "wcs wcscopy_error: "; errmsg += wcscopy_errmsg[err]; throw(AipsError(errmsg)); } 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.ptr()) { 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 { static Vector pixel_tmp1(1); 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 { 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.ptr()) { 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.ptr()) { 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.ptr()) { _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.ptr()) { return _tabular->referencePixel(); } else { Vector crpix(1); crpix[0] = wcs_p.crpix[0]; return crpix; } } Matrix SpectralCoordinate::linearTransform() const { if (_tabular.ptr()) { 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.ptr()) { 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.ptr()) { 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.ptr()) { 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.ptr()) { 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.ptr()) { 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.ptr()) { 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.ptr()) { return _tabular->pixelValues(); } else { Vector pixels; return pixels; } } Vector SpectralCoordinate::worldValues() const { Vector worlds; if (_tabular.ptr()) { 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.ptr() && (_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.ptr()) { 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.ptr()) { 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; int iret = wcsini(1, 1, &wcs); if (iret != 0) { String errmsg = "wcs wcsini_error: "; errmsg += wcsini_errmsg[iret]; throw(AipsError(errmsg)); } // 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 if (int iret = wcsset(&wcs)) { String errmsg = "wcs wcsset_error: "; errmsg += wcsset_errmsg[iret]; throw(AipsError(errmsg)); } } 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.ptr()) { _tabular.reset(new TabularCoordinate(*(other._tabular))); } else { if (_tabular.ptr()) { _tabular.reset(0); } int err = wcscopy (1, &(other.wcs_p), &wcs_p); if (err != 0) { String errmsg = "wcs wcscopy_error: "; errmsg += wcscopy_errmsg[err]; throw(AipsError(errmsg)); } 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.ptr() << 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 _tabular.ptr(); } ostream &operator<<(ostream &os, const SpectralCoordinate& spcoord) { return spcoord.print(os); } } //# NAMESPACE CASACORE - END casacore-2.4.1/coordinates/Coordinates/SpectralCoordinate.h000066400000000000000000000773651321422335000240350ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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: SPtrHolder _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-2.4.1/coordinates/Coordinates/StokesCoordinate.cc000066400000000000000000000364731321422335000236610ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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); } 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-2.4.1/coordinates/Coordinates/StokesCoordinate.h000066400000000000000000000271061321422335000235140ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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-2.4.1/coordinates/Coordinates/TabularCoordinate.cc000066400000000000000000000544521321422335000240000ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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-2.4.1/coordinates/Coordinates/TabularCoordinate.h000066400000000000000000000300661321422335000236350ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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-2.4.1/coordinates/Coordinates/test/000077500000000000000000000000001321422335000210345ustar00rootroot00000000000000casacore-2.4.1/coordinates/Coordinates/test/CMakeLists.txt000066400000000000000000000010111321422335000235650ustar00rootroot00000000000000set (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) target_link_libraries (${test} casa_coordinates) add_test (${test} ${CMAKE_SOURCE_DIR}/cmake/cmake_assay ./${test}) add_dependencies(check ${test}) endforeach (test) casacore-2.4.1/coordinates/Coordinates/test/dCoordinates.cc000066400000000000000000000161371321422335000237710ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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*C::pi/180.0, 60*C::pi/180.0, // 5 -1*C::pi/180.0, 1*C::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 (AipsError x) { cerr << "aipserror: error " << x.getMesg() << endl; return 1; } cout << "ok" << endl; return 0; } casacore-2.4.1/coordinates/Coordinates/test/dCoordinates.out000066400000000000000000000010241321422335000242000ustar00rootroot00000000000000[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-2.4.1/coordinates/Coordinates/test/dRemoveAxes.cc000066400000000000000000000213171321422335000235710ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { cerr << "aipserror: error " << x.getMesg() << endl; return 1; } return 0; } casacore-2.4.1/coordinates/Coordinates/test/dWorldMap.cc000066400000000000000000000225551321422335000232450ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { cerr << "aipserror: error " << x.getMesg() << 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-2.4.1/coordinates/Coordinates/test/tCoordinate.cc000066400000000000000000000053461321422335000236260ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# #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 (AipsError x) { cerr << "aipserror: error " << x.getMesg() << endl; return (1); } cout << "ok" << endl; return (0); } casacore-2.4.1/coordinates/Coordinates/test/tCoordinateSystem.cc000066400000000000000000002620301321422335000250260ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# #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 AipsError& 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 AipsError& 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 AipsError& x) { cerr << "Error " << x.getMesg() << 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 AipsError& 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 (AipsError 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 (AipsError 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 (AipsError 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-2.4.1/coordinates/Coordinates/test/tCoordinateUtil.cc000066400000000000000000000655601321422335000244700ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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 AipsError& x) { cerr << "aipserror: error " << x.getMesg() << 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 (); int main() { 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/C::pi, "deg"); Quantum lat(crval(1)*180.0/C::pi, "deg"); Quantum dlon(cdelt(0)*180.0/C::pi, "deg"); Quantum dlat(cdelt(1)*180.0/C::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(String("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; } { 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 = C::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 AipsError& x) { cerr << "aipserror: error " << x.getMesg() << 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 { Coordinate* pC = dC.makeFourierCoordinate (axes, shape); // Vector units2 = pC->worldAxisUnits(); Vector names2 = pC->worldAxisNames(); Vector crval2 = pC->referenceValue(); Vector crpix2 = pC->referencePixel(); 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")); } 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 = 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)*C::pi/180.0, crval(1)*C::pi/180.0, cdelt(0)*C::pi/180.0, cdelt(1)*C::pi/180.0, xform, crpix(0), crpix(1), longPole*C::pi/180.0, latPole*C::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-2.4.1/coordinates/Coordinates/test/tFrequencyAligner.cc000066400000000000000000000226201321422335000247740ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# #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 (AipsError x) { cerr << "aipserror: error " << x.getMesg() << 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-2.4.1/coordinates/Coordinates/test/tGaussianConvert.cc000066400000000000000000000150331321422335000246440ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# #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 (AipsError x) { cerr << "aipserror: error " << x.getMesg() << 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-2.4.1/coordinates/Coordinates/test/tLinearCoordinate.cc000066400000000000000000000433671321422335000247660ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# #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 (AipsError x) { cerr << "aipserror: error " << x.getMesg() << 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-2.4.1/coordinates/Coordinates/test/tLinearXform.cc000066400000000000000000000233071321422335000237620ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# #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 (AipsError x) { cerr << "aipserror: error " << x.getMesg() << endl; return (1); } cout << "ok" << endl; return (0); } casacore-2.4.1/coordinates/Coordinates/test/tObsInfo.cc000066400000000000000000000240731321422335000230740ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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-2.4.1/coordinates/Coordinates/test/tObsInfo.out000066400000000000000000000003011321422335000233020ustar00rootroot00000000000000Telescope: 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-2.4.1/coordinates/Coordinates/test/tProjection.cc000066400000000000000000000113341321422335000236450ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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 (AipsError x) { cerr << "aipserror: error " << x.getMesg() << endl; return (1); } cout << "ok" << endl; return (0); } casacore-2.4.1/coordinates/Coordinates/test/tQualityCoordinate.cc000066400000000000000000000373351321422335000252020ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# #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 (AipsError x) { cerr << "aipserror: error " << x.getMesg() << 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 (AipsError 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-2.4.1/coordinates/Coordinates/test/tSpectralCoordinate.cc000066400000000000000000001363451321422335000253300ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# #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 AipsError& x) { cerr << "aipserror: error " << x.getMesg() << 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-2.4.1/coordinates/Coordinates/test/tStokesCoordinate.cc000066400000000000000000000301711321422335000250110ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# #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 AipsError& x) { cerr << "aipserror: error " << x.getMesg() << 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 (AipsError 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-2.4.1/coordinates/Coordinates/test/tTabularCoordinate.cc000066400000000000000000000407231321422335000251370ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# #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 (AipsError x) { cerr << "aipserror: error " << x.getMesg() << 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-2.4.1/coordinates/coordinates.dox000066400000000000000000000027331321422335000206360ustar00rootroot00000000000000//# 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: aips2-request@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-2.4.1/derivedmscal/000077500000000000000000000000001321422335000157335ustar00rootroot00000000000000casacore-2.4.1/derivedmscal/CMakeLists.txt000066400000000000000000000014421321422335000204740ustar00rootroot00000000000000# # CASA derivedmscal # add_library (casa_derivedmscal DerivedMC/DerivedMSCal.cc DerivedMC/DerivedColumn.cc DerivedMC/MSCalEngine.cc DerivedMC/Register.cc DerivedMC/UDFMSCal.cc ) target_link_libraries (casa_derivedmscal casa_ms ${CASACORE_ARCH_LIBS}) install (TARGETS casa_derivedmscal 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 DerivedMC/DerivedMSCal.h DerivedMC/DerivedColumn.h DerivedMC/MSCalEngine.h DerivedMC/Register.h DerivedMC/UDFMSCal.h DESTINATION include/casacore/derivedmscal/DerivedMC ) install (FILES DerivedMC.h DESTINATION include/casacore/derivedmscal ) add_subdirectory (DerivedMC/test ${EXCL_ALL}) casacore-2.4.1/derivedmscal/DerivedMC.h000066400000000000000000000100031321422335000177000ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/derivedmscal/DerivedMC/000077500000000000000000000000001321422335000175355ustar00rootroot00000000000000casacore-2.4.1/derivedmscal/DerivedMC/DerivedColumn.cc000066400000000000000000000052211321422335000226040ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# Includes #include namespace casacore { HourangleColumn::~HourangleColumn() {} void HourangleColumn::get (uInt rowNr, Double& data) { data = itsEngine->getHA (itsAntNr, rowNr); } ParAngleColumn::~ParAngleColumn() {} void ParAngleColumn::get (uInt rowNr, Double& data) { data = itsEngine->getPA (itsAntNr, rowNr); } LASTColumn::~LASTColumn() {} void LASTColumn::get (uInt rowNr, Double& data) { data = itsEngine->getLAST (itsAntNr, rowNr); } HaDecColumn::~HaDecColumn() {} IPosition HaDecColumn::shape (uInt) { return IPosition(1,2); } void HaDecColumn::getArray (uInt rowNr, Array& data) { itsEngine->getHaDec (itsAntNr, rowNr, data); } AzElColumn::~AzElColumn() {} IPosition AzElColumn::shape (uInt) { return IPosition(1,2); } void AzElColumn::getArray (uInt rowNr, Array& data) { itsEngine->getAzEl (itsAntNr, rowNr, data); } ItrfColumn::~ItrfColumn() {} IPosition ItrfColumn::shape (uInt) { return IPosition(1,2); } void ItrfColumn::getArray (uInt rowNr, Array& data) { itsEngine->getItrf (itsAntNr, rowNr, data); } UVWJ2000Column::~UVWJ2000Column() {} IPosition UVWJ2000Column::shape (uInt) { return IPosition(1,3); } void UVWJ2000Column::getArray (uInt rowNr, Array& data) { itsEngine->getNewUVW (False, rowNr, data); } } //# end namespace casacore-2.4.1/derivedmscal/DerivedMC/DerivedColumn.h000066400000000000000000000115211321422335000224460ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (uInt 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 (uInt 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 (uInt 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 (uInt rownr); virtual void getArray (uInt 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 (uInt rownr); virtual void getArray (uInt 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 (uInt rownr); virtual void getArray (uInt 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 (uInt rownr); virtual void getArray (uInt rowNr, Array& data); private: MSCalEngine* itsEngine; }; } //# end namespace #endif casacore-2.4.1/derivedmscal/DerivedMC/DerivedMSCal.cc000066400000000000000000000121521321422335000223070ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (uInt 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-2.4.1/derivedmscal/DerivedMC/MSCalEngine.cc000066400000000000000000000417421321422335000221410ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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, uInt rownr) { setData (antnr, rownr); return itsRADecToHADec().getValue().get()[0]; } void MSCalEngine::getHaDec (Int antnr, uInt rownr, Array& data) { setData (antnr, rownr); data = itsRADecToHADec().getValue().get(); } double MSCalEngine::getPA (Int antnr, uInt 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, uInt rownr) { setData (antnr, rownr); return itsUTCToLAST().getValue().get(); } void MSCalEngine::getAzEl (Int antnr, uInt rownr, Array& data) { setData (antnr, rownr); data = itsRADecToAzEl().getValue().get(); } void MSCalEngine::getItrf (Int antnr, uInt rownr, Array& data) { setData (antnr, rownr); data = itsRADecToItrf().getValue().get(); } void MSCalEngine::getNewUVW (Bool asApp, uInt 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, uInt 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, uInt 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 (uInt 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 (uInt i=fieldDir.size(); i nameCol(tab, "MS_NAME"); // Handle CAL_DESC_IDs not seen so far. itsCalIdMap.reserve (tab.nrow()); for (uInt 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-2.4.1/derivedmscal/DerivedMC/MSCalEngine.h000066400000000000000000000226441321422335000220030ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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, uInt rownr); // Get the hourangle/DEC for the given row. void getHaDec (Int antnr, uInt rownr, Array&); // Get the parallatic angle for the given row. double getPA (Int antnr, uInt rownr); // Get the local sidereal time for the given row. double getLAST (Int antnr, uInt rownr); // Get the azimuth/elevation for the given row. void getAzEl (Int antnr, uInt rownr, Array&); // Get the ITRF coordinates for the given row. void getItrf (Int antnr, uInt rownr, Array&); // Get the UVW in J2000 or APP for the given row. void getNewUVW (Bool asApp, uInt rownr, Array&); // Get the delay for the given row. double getDelay (Int antnr, uInt 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, uInt 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-2.4.1/derivedmscal/DerivedMC/Register.cc000066400000000000000000000341011321422335000216270ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 << " double MSCAL.HADEC() " " hourangle/declination of array center" << endl; os << " double MSCAL.HADEC1() " " hourangle/declination of ANTENNA1" << endl; os << " double 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-2.4.1/derivedmscal/DerivedMC/Register.h000066400000000000000000000044701321422335000214770ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/derivedmscal/DerivedMC/UDFMSCal.cc000066400000000000000000001031521321422335000213440ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (TableExprNodeRep*& 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, PtrBlock& 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 (uInt 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, PtrBlock& 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 (ROArrayColumn(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, PtrBlock& 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; MSSelectionErrorHandler* curHandler = MSAntennaParse::thisMSAErrorHandler; UDFMSCalErrorHandler errorHandler; MSAntennaParse::thisMSAErrorHandler = &errorHandler; try { itsDataNode = msAntennaGramParseCommand (anttab, a1, a2, selStr, selectedAnts1, selectedAnts2, selectedBaselines); } catch (const std::exception&) { MSAntennaParse::thisMSAErrorHandler = curHandler; throw; } MSAntennaParse::thisMSAErrorHandler = 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; MSSelectionErrorHandler* curHandler = MSFeedParse::thisMSFErrorHandler; UDFMSCalErrorHandler errorHandler; MSFeedParse::thisMSFErrorHandler = &errorHandler; try { itsDataNode = msFeedGramParseCommand (feedtab, f1, f2, selStr, selectedFeed1, selectedFeed2, selectedFeedPairs); } catch (const std::exception&) { MSFeedParse::thisMSFErrorHandler = curHandler; throw; } MSFeedParse::thisMSFErrorHandler = 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; MSSelectionErrorHandler* curHandler = MSStateParse::thisMSSErrorHandler; UDFMSCalErrorHandler errorHandler; MSStateParse::thisMSSErrorHandler = &errorHandler; try { if (msStateGramParseCommand(&ms, selStr, stateid) == 0) { itsDataNode = *(msStateGramParseNode()); } } catch (const std::exception&) { msStateGramParseDeleteNode(); MSStateParse::thisMSSErrorHandler = curHandler; throw; } MSStateParse::thisMSSErrorHandler = 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, PtrBlock& 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: { Int64 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: { Int64 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: { Int64 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: { Int64 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-2.4.1/derivedmscal/DerivedMC/UDFMSCal.h000066400000000000000000000272331321422335000212130ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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, PtrBlock& operands); // Setup the baseline selection. void setupSelection (const Table& table, PtrBlock& operands); // Setup direction conversion if a direction is explicitly given. void setupDir (TableExprNodeRep*& operand); // Setup getting column values from a subtable. void setupGetValue (const Table& table, PtrBlock& operands); // Setup getting the wavelength information. void setupWvls (const Table& table, PtrBlock& 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: virtual ~UDFMSCalErrorHandler() {} virtual void handleError (MSSelectionError&) {} virtual void reportError (const char*, const String) {} }; } //end namespace #endif casacore-2.4.1/derivedmscal/DerivedMC/test/000077500000000000000000000000001321422335000205145ustar00rootroot00000000000000casacore-2.4.1/derivedmscal/DerivedMC/test/CMakeLists.txt000066400000000000000000000004311321422335000232520ustar00rootroot00000000000000set (tests tDerivedMSCal tUDFMSCal ) foreach (test ${tests}) add_executable (${test} ${test}.cc) target_link_libraries (${test} casa_derivedmscal) add_test (${test} ${CMAKE_SOURCE_DIR}/cmake/cmake_assay ./${test}) add_dependencies(check ${test}) endforeach (test) casacore-2.4.1/derivedmscal/DerivedMC/test/tDerivedMSCal.cc000066400000000000000000000206721321422335000234600ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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)); } 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; } 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)); } } 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) { ROMSAntennaColumns 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. uInt nr = std::max(tab.nrow(), 1000u); Int lastFldId = -1; for (uInt 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-2.4.1/doxygen.cfg000066400000000000000000003040741321422335000154370ustar00rootroot00000000000000# 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" # This tag can be used to specify a number of word-keyword mappings (TCL only). # A mapping has the form "name=value". For example adding "class=itcl::class" # will allow you to use the command class in the itcl::class meaning. TCL_SUBST = # 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. LOOKUP_CACHE_SIZE = 0 #--------------------------------------------------------------------------- # 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/* # 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 # The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in # which the alphabetical index list will be split. # Minimum value: 1, maximum value: 20, default value: 5. # This tag requires that the tag ALPHABETICAL_INDEX is set to YES. COLS_IN_ALPHA_INDEX = 3 # 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-2.4.1/fits/FITS/000077500000000000000000000000001321422335000150035ustar00rootroot00000000000000casacore-2.4.1/fits/FITS/BasicFITS.cc000066400000000000000000000211261321422335000170230ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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, 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 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) { ConstMapIter keyiter(keywords); String key; Double val; while (! keyiter.atEnd()) { key = keyiter.getKey(); val = keyiter.getVal(); // 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); ++keyiter; } } 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-2.4.1/fits/FITS/BasicFITS.h000066400000000000000000000143201321422335000166630ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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, 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 Map *keywords = 0, const char *objectName = 0, Int BITPIX=-32, Float minPix = 1.0, Float maxPix = -1.0); // } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/fits/FITS/BinTable.cc000066400000000000000000000767021321422335000170060ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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()) { sprintf(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 SimpleOrderedMap(""); 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->define(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-2.4.1/fits/FITS/BinTable.h000066400000000000000000000155621321422335000166450ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 SimpleOrderedMap *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-2.4.1/fits/FITS/CopyRecord.cc000066400000000000000000001053571321422335000173760ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/fits/FITS/CopyRecord.h000066400000000000000000000215461321422335000172350ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #ifndef FITS_COPYRECORD_H #define FITS_COPYRECORD_H #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class Table; class TableDesc; class RecordInterface; class RecordDesc; class String; template class Vector; 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-2.4.1/fits/FITS/FITS2.h000066400000000000000000000050561321422335000160110ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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, Map *keywords, String *objectName); // } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-2.4.1/fits/FITS/FITS2.tcc000066400000000000000000000110201321422335000163170ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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, 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-2.4.1/fits/FITS/FITSDateUtil.cc000066400000000000000000000151361321422335000175210ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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-2.4.1/fits/FITS/FITSError.cc000066400000000000000000000033231321422335000170720ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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-2.4.1/fits/FITS/FITSError.h000066400000000000000000000101671321422335000167400ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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-2.4.1/fits/FITS/FITSFieldCopier.h000066400000000000000000000210021321422335000200220ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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-2.4.1/fits/FITS/FITSHistoryUtil.cc000066400000000000000000000243731321422335000203100ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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-2.4.1/fits/FITS/FITSHistoryUtil.h000066400000000000000000000156421321422335000201510ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #ifndef FITS_FITSHISTORYUTIL_H #define FITS_FITSHISTORYUTIL_H #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN class ConstFitsKeywordList; class FitsKeywordList; class String; template class Vector; 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-2.4.1/fits/FITS/FITSKeywordUtil.cc000066400000000000000000001026521321422335000202700ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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. SimpleOrderedMap min1D(99999), max1D(0), min2Drow(99999), min2Dcol(99999), max2Drow(0), max2Dcol(0); // 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 (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 (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-2.4.1/fits/FITS/FITSKeywordUtil.h000066400000000000000000000207361321422335000201340ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #ifndef FITS_FITSKEYWORDUTIL_H #define FITS_FITSKEYWORDUTIL_H #include namespace casacore { //# NAMESPACE CASACORE - BEGIN class ConstFitsKeywordList; class FitsKeywordList; class RecordInterface; class IPosition; class String; template class Vector; // // 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-2.4.1/fits/FITS/FITSMultiTable.cc000066400000000000000000000205001321422335000200370ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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, String("^[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]") + ".*" + String(".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-2.4.1/fits/FITS/FITSMultiTable.h000066400000000000000000000115301321422335000177040ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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-2.4.1/fits/FITS/FITSReader.cc000066400000000000000000000640621321422335000172120ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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; } 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-2.4.1/fits/FITS/FITSReader.h000066400000000000000000000034771321422335000170570ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: FITSReader.h 2012-01-31$ #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-2.4.1/fits/FITS/FITSSpectralUtil.cc000066400000000000000000000564271321422335000204310ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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-2.4.1/fits/FITS/FITSSpectralUtil.h000066400000000000000000000222561321422335000202640ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #ifndef FITS_FITSSPECTRALUTIL_H #define FITS_FITSSPECTRALUTIL_H #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN class RecordInterface; class String; template class Vector; 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-2.4.1/fits/FITS/FITSTable.cc000066400000000000000000001616161321422335000170420ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 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-2.4.1/fits/FITS/FITSTable.h000066400000000000000000000430151321422335000166740ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #ifndef FITS_FITSTABLE_H #define FITS_FITSTABLE_H #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN class String; class FITSFieldCopier; class TableDesc; template class Vector; // // 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-2.4.1/fits/FITS/FITSTable2.cc000066400000000000000000000460151321422335000171170ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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); } 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); } 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); } 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); } 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); } 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); } 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); } 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); } 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-2.4.1/fits/FITS/FITSTable3.cc000066400000000000000000000227411321422335000171200ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/fits/FITS/FITSTimedTable.cc000066400000000000000000000233641321422335000200220ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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-2.4.1/fits/FITS/FITSTimedTable.h000066400000000000000000000114221321422335000176540ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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-2.4.1/fits/FITS/SDFITSTable.cc000066400000000000000000000117231321422335000172620ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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-2.4.1/fits/FITS/blockio.cc000066400000000000000000000344211321422335000167400ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ // # 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-2.4.1/fits/FITS/blockio.h000066400000000000000000000157011321422335000166020ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/fits/FITS/fits.cc000066400000000000000000002357371321422335000163000ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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 = C::flt_min; const float FITS::maxfloat = C::flt_max; const double FITS::mindouble = C::dbl_min; const double FITS::maxdouble = C::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), FitsCardSize(80), FitsMaxCard(36), FitsRecSize(2880), 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: sprintf(&card[18],"%12d",k.asInt()); card[30] = ' '; break; case FITS::FLOAT: sprintf(&card[16],"%#14.7E",k.asFloat()); card[30] = ' '; break; case FITS::DOUBLE: sprintf(&card[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: sprintf(&card[18],"%12d",k.asIComplex().real()); card[30] = ' '; sprintf(&card[38],"%12d",k.asIComplex().imag()); card[50] = ' '; break; case FITS::COMPLEX: sprintf(&card[16],"%#14.6E",k.asComplex().real()); sprintf(&card[36],"%#14.6E",k.asComplex().imag()); card[50] = ' '; break; case FITS::DCOMPLEX: sprintf(&card[10],"%#20.12E",k.asDComplex().real()); sprintf(&card[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(const FitsLogical &x) : v(x.v) { } FitsLogical & operator = (const FitsLogical &x) { v = x.v; return *this; } FitsLogical & operator = (Bool x) { v = (x == True ? 'T' : 'F'); return *this; } Bool isdefined() const { return v == '\0' ? True : False; } void undefine() { v = '\0'; } operator Bool() { return (v == 'T' ? True : False); } 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(const FitsBit &x) : bit_array(x.bit_array) { } FitsBit & operator = (const FitsBit &x) { bit_array = x.bit_array; return *this; } FitsBit & operator = (unsigned char x) { bit_array = x; return *this; } operator unsigned char() { 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 const int FitsCardSize; const int FitsMaxCard; const int FitsRecSize; 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-2.4.1/fits/FITS/fitsio.cc000066400000000000000000001472551321422335000166250ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ # 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; } } // 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 sprintf( 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-2.4.1/fits/FITS/fitsio.h000066400000000000000000000255611321422335000164620ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/fits/FITS/hdu.h000066400000000000000000001236321321422335000157430ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 &); 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-2.4.1/fits/FITS/hdu.tcc000066400000000000000000001462601321422335000162670ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/fits/FITS/hdu2.cc000066400000000000000000002356471321422335000161750ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ # 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::operator = (FitsBase &x) { if (fieldtype() == x.fieldtype() && nelements() == x.nelements()) memcpy(data(),x.data(),localfieldsize()); return *this; } //==================================================================================== 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: sprintf(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: sprintf(tmp,format[i],*((float *)(fld[i]->data()))); memcpy(&fitsrow[fits_offset[i]],tmp,fits_width[i]); break; case FITS::DOUBLE: sprintf(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-2.4.1/fits/FITS/test/000077500000000000000000000000001321422335000157625ustar00rootroot00000000000000casacore-2.4.1/fits/FITS/test/CMakeLists.txt000066400000000000000000000010051321422335000205160ustar00rootroot00000000000000set (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) target_link_libraries (${test} casa_fits) add_test (${test} ${CMAKE_SOURCE_DIR}/cmake/cmake_assay ./${test}) add_dependencies(check ${test}) endforeach (test) casacore-2.4.1/fits/FITS/test/tBinTable.cc000066400000000000000000000104461321422335000201420ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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 char *tabName = new char[baseName.length() + 10]; // 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: { sprintf(tabName,"%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 (AipsError x) { cout << "Unexpected exception: " << x.getMesg() << endl; return 1; } return 0; } casacore-2.4.1/fits/FITS/test/tBinTable.run000066400000000000000000000006671321422335000203650ustar00rootroot00000000000000#!/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-2.4.1/fits/FITS/test/tFITS.cc000066400000000000000000000072521321422335000172300ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# Includes #if !defined(AIPS_DEBUG) #define AIPS_DEBUG #endif #include #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 OrderedMap mapout(0.0), mapin(0.0); 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-2.4.1/fits/FITS/test/tFITSDateUtil.cc000066400000000000000000000112631321422335000206610ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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 (AipsError x) { cout << "Caught an exception: " << x.getMesg() << endl; return 1; } return 0; } casacore-2.4.1/fits/FITS/test/tFITSHistoryUtil.cc000066400000000000000000000116031321422335000214430ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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); 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 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 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 (AipsError x) { cerr << "Caught an exception: " << x.getMesg() << endl; return 1; } return 0; } casacore-2.4.1/fits/FITS/test/tFITSSpectralUtil.cc000066400000000000000000000310051321422335000215550ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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 (AipsError x) { cout << "Caught an exception: " << x.getMesg() << 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 (AipsError x) { cout << "Caught an exception: " << x.getMesg() << endl; return 1; } return 0; } casacore-2.4.1/fits/FITS/test/t_priArr_imgExt.cc000066400000000000000000000105151321422335000213720ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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-2.4.1/fits/FITS/test/tfits1.cc000066400000000000000000000364311321422335000175120ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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-2.4.1/fits/FITS/test/tfits2.cc000066400000000000000000000072511321422335000175110ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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-2.4.1/fits/FITS/test/tfits3.cc000066400000000000000000000056431321422335000175150ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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-2.4.1/fits/FITS/test/tfits4.cc000066400000000000000000000163431321422335000175150ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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-2.4.1/fits/FITS/test/tfits5.cc000066400000000000000000000217261321422335000175170ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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: sprintf(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-2.4.1/fits/FITS/test/tfits_ascTbl.cc000066400000000000000000000135051321422335000207160ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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-2.4.1/fits/FITS/test/tfits_binTbl2.cc000066400000000000000000000135711321422335000210050ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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-2.4.1/fits/FITS/test/tfits_imgExt2.cc000066400000000000000000000077061321422335000210330ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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-2.4.1/fits/FITS/test/tfits_priGrp.cc000066400000000000000000000064161321422335000207540ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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-2.4.1/fits/FITS/test/tfitsread_data.cc000066400000000000000000000366141321422335000212610ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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-2.4.1/fits/FITS/test/tfitsreader.cc000066400000000000000000000031701321422335000206060ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ # 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-2.4.1/fits/FITS/test/tfitsskip.cc000066400000000000000000000372531321422335000203230ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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-2.4.1/fits/FITS/test/tfitsskip_all.cc000066400000000000000000000371531321422335000211520ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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-2.4.1/fits/FITS/test/tfitsskip_hdu.cc000066400000000000000000000362611321422335000211610ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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-2.4.1/fits/apps/000077500000000000000000000000001321422335000152015ustar00rootroot00000000000000casacore-2.4.1/fits/apps/CMakeLists.txt000066400000000000000000000002451321422335000177420ustar00rootroot00000000000000add_executable (fits2table fits2table/fits2table.cc) target_link_libraries (fits2table casa_fits ${CASACORE_ARCH_LIBS}) install(TARGETS fits2table DESTINATION bin) casacore-2.4.1/fits/apps/fits2table/000077500000000000000000000000001321422335000172405ustar00rootroot00000000000000casacore-2.4.1/fits/apps/fits2table/fits2table.cc000066400000000000000000000106621321422335000216130ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: fits2table.cc,v 19.3 2004/11/30 17:50:07 ddebonis Exp $ //# 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 (AipsError x) { cout << "Exception: " << x.getMesg() << endl; return 1; } return 0; } casacore-2.4.1/fits/fits.dox000066400000000000000000000026421321422335000157230ustar00rootroot00000000000000//# 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: aips2-request@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-2.4.1/images/000077500000000000000000000000001321422335000145365ustar00rootroot00000000000000casacore-2.4.1/images/CMakeLists.txt000066400000000000000000000076571321422335000173150ustar00rootroot00000000000000# # CASA Images # BISON_TARGET (ImageExprGram Images/ImageExprGram.yy ${CMAKE_CURRENT_BINARY_DIR}/ImageExprGram.ycc COMPILE_FLAGS "-p ImageExprGram") 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} ) target_link_libraries ( casa_images casa_coordinates casa_mirlib casa_lattices ${CASACORE_ARCH_LIBS} ) add_subdirectory (apps) install ( TARGETS casa_images 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 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 Images/WCBox.h 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 ) # Temporary hack until all packages use scimath/GaussianBeam install (FILES Images/GaussianBeam.h DESTINATION include/casacore/components/ComponentModels ) install (FILES Regions.h Images.h DESTINATION include/casacore/images ) add_subdirectory (Images/test ${EXCL_ALL}) add_subdirectory (Regions/test ${EXCL_ALL}) casacore-2.4.1/images/Images.h000066400000000000000000000062571321422335000161260ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/images/Images/000077500000000000000000000000001321422335000157435ustar00rootroot00000000000000casacore-2.4.1/images/Images/CurvedImage2D.h000066400000000000000000000220741321422335000205020ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/images/Images/CurvedImage2D.tcc000066400000000000000000000155461321422335000210320ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/images/Images/ExtendImage.h000066400000000000000000000147441321422335000203200ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: ExtendImage.h 21538 2015-01-07 09:08:57Z gervandiepen $ #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. PtrHolder > itsImagePtr; PtrHolder > 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-2.4.1/images/Images/ExtendImage.tcc000066400000000000000000000147561321422335000206450ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: ExtendImage.tcc 21563 2015-02-16 07:05:15Z gervandiepen $ #ifndef IMAGES_EXTENDIMAGE_TCC #define IMAGES_EXTENDIMAGE_TCC #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template ExtendImage::ExtendImage() : itsImagePtr (0), itsExtLatPtr (0) {} 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.set(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.set(other.itsImagePtr->cloneII()); itsExtLatPtr.set(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-2.4.1/images/Images/FITS2Image.tcc000066400000000000000000000304441321422335000202350ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: #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 T* t=0; DataType dataType = whatType(t); // 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 T* t=0; DataType dataType = whatType(t); // 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-2.4.1/images/Images/FITSErrorImage.cc000066400000000000000000000123741321422335000210030ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/images/Images/FITSErrorImage.h000066400000000000000000000106121321422335000206360ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef IMAGES_FITSERRORIMAGE_H #define IMAGES_FITSERRORIMAGE_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations template class Array; // 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-2.4.1/images/Images/FITSImage.cc000066400000000000000000000571721321422335000177760ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include #include #include #include #include #include #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), pPixelMask_p(0), 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), pPixelMask_p(0), 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), pPixelMask_p(0), 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 != 0) { pPixelMask_p = 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; // 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; 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; } FITSImage::~FITSImage() { delete pPixelMask_p; } 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 "FITSImage"; } 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) { delete pPixelMask_p; pPixelMask_p = 0; // pTiledFile_p = 0; 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 = new TiledFileAccess(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)); } else if (dataType_p == TpDouble) { fitsMask = new FITSMask(&(*pTiledFile_p)); } else if (dataType_p == TpUChar) { fitsMask = new FITSMask(&(*pTiledFile_p), scale_p, offset_p, uCharMagic_p, hasBlanks_p); } else if (dataType_p == TpShort) { fitsMask = new FITSMask(&(*pTiledFile_p), scale_p, offset_p, shortMagic_p, hasBlanks_p); } else if (dataType_p == TpInt) { fitsMask = new FITSMask(&(*pTiledFile_p), scale_p, offset_p, longMagic_p, hasBlanks_p); } if (fitsMask) { fitsMask->setFilterZero(filterZeroMask_p); pPixelMask_p = fitsMask; } } // 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)->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-2.4.1/images/Images/FITSImage.h000066400000000000000000000253361321422335000176350ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef IMAGES_FITSIMAGE_H #define IMAGES_FITSIMAGE_H //# Includes #include #include #include #include #include #include #include #include #ifndef WCSLIB_GETWCSTAB #define WCSLIB_GETWCSTAB #endif namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations template class Array; 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(); // 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; // 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; CountedPtr pTiledFile_p; Lattice* 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-2.4.1/images/Images/FITSImgParser.cc000066400000000000000000000563031321422335000206400ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/images/Images/FITSImgParser.h000066400000000000000000000176271321422335000205100ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/images/Images/FITSQualityImage.cc000066400000000000000000000525571321422335000213510ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include #include #include #include #include #include #include #include #include #include #include #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-2.4.1/images/Images/FITSQualityImage.h000066400000000000000000000212041321422335000211740ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef IMAGES_FITSQUALITYIMAGE_H #define IMAGES_FITSQUALITYIMAGE_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations template class Array; 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-2.4.1/images/Images/FITSQualityMask.cc000066400000000000000000000166771321422335000212250ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/images/Images/GaussianBeam.h000066400000000000000000000002071321422335000204520ustar00rootroot00000000000000//# Will be installed in components/ComponentModels for backward compatibility. #include casacore-2.4.1/images/Images/HDF5Image.h000066400000000000000000000325741321422335000175600ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef IMAGES_HDF5IMAGE_H #define IMAGES_HDF5IMAGE_H //# Includes #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 CountedPtr& 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-2.4.1/images/Images/HDF5Image.tcc000066400000000000000000000337061321422335000201000ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 CountedPtr& 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-2.4.1/images/Images/HDF5Image2.cc000066400000000000000000000042061321422335000177670ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError& 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 (AipsError& x) { // Nothing } } return retval; } } //# NAMESPACE CASACORE - END casacore-2.4.1/images/Images/ImageAttrGroup.cc000066400000000000000000000026071321422335000211510ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# Includes #include namespace casacore { ImageAttrGroup::~ImageAttrGroup() {} } //# NAMESPACE CASACORE - END casacore-2.4.1/images/Images/ImageAttrGroup.h000066400000000000000000000143701321422335000210130ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/images/Images/ImageAttrGroupCasa.cc000066400000000000000000000167531321422335000217500ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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) { ROTableColumn col(itsTable.table(), attrName); if (col.keywordSet().isDefined("QuantumUnits")) { return col.keywordSet().asArrayString("QuantumUnits"); } return Vector(); } Vector ImageAttrGroupCasa::getMeasInfo (const String& attrName) { ROTableColumn 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-2.4.1/images/Images/ImageAttrGroupCasa.h000066400000000000000000000105161321422335000216010ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/images/Images/ImageAttrGroupHDF5.cc000066400000000000000000000137001321422335000215540ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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-2.4.1/images/Images/ImageAttrGroupHDF5.h000066400000000000000000000110271321422335000214160ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/images/Images/ImageAttrHandler.cc000066400000000000000000000041221321422335000214240ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# Includes #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-2.4.1/images/Images/ImageAttrHandler.h000066400000000000000000000105321321422335000212700ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/images/Images/ImageAttrHandlerCasa.cc000066400000000000000000000117251321422335000222230ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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-2.4.1/images/Images/ImageAttrHandlerCasa.h000066400000000000000000000110751321422335000220630ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/images/Images/ImageAttrHandlerHDF5.cc000066400000000000000000000112751321422335000220420ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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 = CountedPtr(new HDF5Group(hid, "ATTRGROUPS", true)); vector names = HDF5Group::linkNames (*itsGroup); for (uInt i=0; i(new HDF5Group(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-2.4.1/images/Images/ImageAttrHandlerHDF5.h000066400000000000000000000121501321422335000216750ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef IMAGES_IMAGEATTRHANDLERHDF5_H #define IMAGES_IMAGEATTRHANDLERHDF5_H //# Includes #include #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? CountedPtr itsGroup; //# HDF5 group to add to std::map itsGroupMap; //# attribute groups }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/images/Images/ImageBeamSet.cc000066400000000000000000000553231321422335000205450ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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) { _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) { _beams(chan, stokes) = beam; IPosition location(2, chan, stokes); if (location == _maxBeamPos || location == _minBeamPos) { // we are overwriting the max or min beam, so we need to // determine the new max or min _calculateAreas(); } else { Double area = beam.getArea(_areaUnit); _areas(chan, stokes) = area; if (area < _areas(_minBeamPos)) { _minBeam = beam; _minBeamPos = location; } if (area > _areas(_maxBeamPos)) { _maxBeam = beam; _maxBeamPos = location; } } } else if (chan < 0 && stokes < 0) { *this = ImageBeamSet(beam); } else if (chan < 0) { _beams(IPosition(2, 0, stokes), IPosition(2, nchan()-1, stokes)) = beam; if (_maxBeamPos[0] == chan || _minBeamPos[0] == chan) { // we are overwriting the max or min beam, so we need to recalculate // the areas _calculateAreas(); } else { Double area = beam.getArea(_areaUnit); _areas(IPosition(2, 0, stokes), IPosition(2, nchan()-1, stokes)) = area; if (area < _areas(_minBeamPos)) { _minBeam = beam; _minBeamPos = IPosition(2, 0, stokes); } if (area > _areas(_maxBeamPos)) { _maxBeam = beam; _maxBeamPos = IPosition(2, 0, stokes); } } } else { // chan >=0 && stokes < 0 _beams(IPosition(2, chan, 0), IPosition(2, chan, nstokes()-1)) = beam; if (_maxBeamPos[1] == stokes || _minBeamPos[1] == stokes) { // we are overwriting the max or min beam, so we need to recalculate // the areas _calculateAreas(); } else { Double area = beam.getArea(_areaUnit); _areas(IPosition(2, chan, 0), IPosition(2, chan, nstokes()-1)) = area; if (area < _areas(_minBeamPos)) { _minBeam = beam; _minBeamPos = IPosition(2, chan, 0); } if (area > _areas(_maxBeamPos)) { _maxBeam = beam; _maxBeamPos = IPosition(2, chan, 0); } } } } 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" ); uInt nchan = rec.asuInt("nChannels"); ImageBeamSet beams(nchan, rec.asuInt("nStokes")); uInt count = 0; uInt chan = 0; uInt stokes = 0; Array::const_iterator iterend = beams.getBeams().end(); for ( Array::const_iterator iter = beams.getBeams().begin(); iter != iterend; ++iter, ++count ) { String field = "*" + String::toString(count); ThrowIf( ! rec.isDefined(field), "Field " + field + " is not defined" ); beams.setBeam( chan, stokes, GaussianBeam::fromRecord(rec.asRecord(field)) ); if (++chan == nchan) { chan = 0; stokes++; } } return beams; } Record ImageBeamSet::toRecord() const { Record perPlaneBeams; perPlaneBeams.define("nChannels", nchan()); perPlaneBeams.define("nStokes", nstokes()); Record rec; uInt count = 0; const Array& beams = getBeams(); Array::const_iterator iterEnd = beams.end(); for ( Array::const_iterator iter=beams.begin(); iter!=iterEnd; ++iter, ++count ) { ThrowIf( iter->isNull(), "Invalid per plane beam found" ); Record rec = iter->toRecord(); perPlaneBeams.defineRecord("*" + String::toString(count), rec); } 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; } } } } 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-2.4.1/images/Images/ImageBeamSet.h000066400000000000000000000256361321422335000204130ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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); private: static const String _DEFAULT_AREA_UNIT; Matrix _beams; Matrix _areas; String _areaUnit; GaussianBeam _minBeam, _maxBeam; IPosition _minBeamPos, _maxBeamPos; void _calculateAreas(); static void _chanInfoToStream( ostream& os, const SpectralCoordinate *spCoord, const uInt chan, const uInt chanWidth, const uInt freqPrec, const uInt velWidth, const uInt velPrec ); static void _beamToStream( ostream& os, const GaussianBeam& beam, const Unit& unit ); }; ostream &operator<<(ostream &os, const ImageBeamSet& beamSet); } #endif casacore-2.4.1/images/Images/ImageConcat.h000066400000000000000000000262361321422335000202770ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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); // 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(); } // 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. This isn't very meaningful for an ImageConcat // Image 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 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-2.4.1/images/Images/ImageConcat.tcc000066400000000000000000000705311321422335000206160ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: ImageConcat.tcc 21563 2015-02-16 07:05:15Z gervandiepen $ #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 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::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 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->setImageInfo (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 latticeConcat_p.niceCursorShape(maxPixels); } 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-2.4.1/images/Images/ImageExpr.h000066400000000000000000000177041321422335000200060ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef IMAGES_IMAGEEXPR_H #define IMAGES_IMAGEEXPR_H //# Includes #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class JsonKVMap; class IPosition; class Slicer; template class Array; 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-2.4.1/images/Images/ImageExpr.tcc000066400000000000000000000205761321422335000203310ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/images/Images/ImageExprGram.cc000066400000000000000000000106351321422335000207470ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ // 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 // 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). #include //# Define register as empty string to avoid warnings in C++11 compilers //# because keyword register is not supported anymore. #define register #include "ImageExprGram.ycc" // bison output #include "ImageExprGram.lcc" // flex output // 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; //# Parse the command. //# Do a yyrestart(yyin) first to make the flex scanner reentrant. int imageExprGramParseCommand (const String& command) { ImageExprGramrestart (ImageExprGramin); yy_start = 1; // Save global state for re-entrancy. const char* savStrpImageExprGram = strpImageExprGram; Int savPosImageExprGram= posImageExprGram; strpImageExprGram = command.chars(); // get pointer to command string posImageExprGram = 0; // initialize string position int sts = ImageExprGramparse(); // parse command string // Restore global state. strpImageExprGram = savStrpImageExprGram; posImageExprGram= savPosImageExprGram; 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-2.4.1/images/Images/ImageExprGram.ll000066400000000000000000000213331321422335000207660ustar00rootroot00000000000000/* 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: aips2-request@nrao.edu. Postal address: AIPS++ Project Office National Radio Astronomy Observatory 520 Edgemont Road Charlottesville, VA 22903-2475 USA $Id$ */ /* 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-2.4.1/images/Images/ImageExprGram.yy000066400000000000000000000175251321422335000210300ustar00rootroot00000000000000/* 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: aips2-request@nrao.edu. Postal address: AIPS++ Project Office National Radio Astronomy Observatory 520 Edgemont Road Charlottesville, VA 22903-2475 USA $Id$ */ %{ 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-2.4.1/images/Images/ImageExprParse.cc000066400000000000000000000657701321422335000211450ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 CountedPtr 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; \ CountedPtr 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.null()); } //# 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 CountedPtr& 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 (AipsError x) { message = x.getMesg(); 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 (C::pi); } else if (val == "e") { return LatticeExprNode (C::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 { // When 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). // Test if it is a region. Vector names = stringToVector (itsSval, ':'); if (names.nelements() != 1) { if ((names.nelements() == 2 && names(1).empty()) || (names.nelements() == 3 && !names(1).empty() && names(2).empty()) || names.nelements() > 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.nelements() == 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.nelements() == 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.nelements() == 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 = new HDF5File(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.nelements() == 1 ? 0 : 2); if (! theLastTable.isNull()) { RegionHandlerTable regHand(getRegionTable, 0); regPtr = regHand.getRegion (names(index), RegionHandler::Any, False); } if (! theLastHDF5.null()) { 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 = new HDF5File(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?). /// if (itsSval == "pi") { /// return LatticeExprNode (C::pi); /// } else if (itsSval == "e") { /// return LatticeExprNode (C::e); /// } // It is a 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-2.4.1/images/Images/ImageExprParse.h000066400000000000000000000332361321422335000207770ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef IMAGES_IMAGEEXPRPARSE_H #define IMAGES_IMAGEEXPRPARSE_H //# Includes #include #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 CountedPtr& 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-2.4.1/images/Images/ImageFITS2Converter.cc000066400000000000000000001654311321422335000217460ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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 AipsError& 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 (AipsError x) { msg = x.getMesg(); } // 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 // I should FITS-ize the units 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 uInt nmisc = image.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 = image.miscInfo().dataType(i); switch(misctype) { case TpBool: header.define(miscname, image.miscInfo().asBool(i)); break; case TpChar: case TpUChar: case TpShort: case TpUShort: case TpInt: case TpUInt: header.define(miscname, image.miscInfo().asInt(i)); break; case TpFloat: header.define(miscname, image.miscInfo().asfloat(i)); break; case TpDouble: header.define(miscname, image.miscInfo().asdouble(i)); break; case TpComplex: header.define(miscname, image.miscInfo().asComplex(i)); break; case TpDComplex: header.define(miscname, image.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, image.miscInfo().asString(i))) { // Conversion worked - change the header header.define(miscname, outdate); } else { // conversion failed - just copy the existing date header.define(miscname, image.miscInfo().asString(i)); } } else { // Just copy non-date strings through header.define(miscname, image.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, image.miscInfo().asArrayBool(i)); break; case TpArrayChar: case TpArrayUShort: case TpArrayInt: case TpArrayUInt: case TpArrayInt64: header.define(miscname, image.miscInfo().toArrayInt(i)); break; case TpArrayFloat: header.define(miscname, image.miscInfo().asArrayfloat(i)); break; case TpArrayDouble: header.define(miscname, image.miscInfo().asArraydouble(i)); break; case TpArrayString: header.define(miscname, image.miscInfo().asArrayString(i)); break; default: { ostringstream os; os << misctype; os << LogIO::NORMAL << "Not writing miscInfo field '" << miscname << "' - cannot handle type " << String(os) << 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 AipsError& 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-2.4.1/images/Images/ImageFITSConverter.h000066400000000000000000000423771321422335000215310ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #ifndef IMAGES_IMAGEFITSCONVERTER_H #define IMAGES_IMAGEFITSCONVERTER_H #include #include #include #include #include #include #ifndef WCSLIB_GETWCSTAB #define WCSLIB_GETWCSTAB #endif namespace casacore { //# NAMESPACE CASACORE - BEGIN template class PagedImage; template class ImageInterface; template class Vector; 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; CountedPtr > 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-2.4.1/images/Images/ImageFITSConverter.tcc000066400000000000000000000251711321422335000220440ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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; LatticeIterator* pMaskIter = 0; 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 = new LatticeIterator(mask, pMaskStepper); pMaskIter->reset(); 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 (uInt i=0; ioperator++(); } else { if (zeroBlanks) { for (uInt i=0; idefineRegion ("mask0", maskReg, RegionHandler::Masks); pNewImage->setDefaultMask(String("mask0")); } // Clean up pointers delete pMaskIter; } } 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-2.4.1/images/Images/ImageInfo.cc000066400000000000000000000623761321422335000201260ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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; } } 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 AipsError& x) { error(0) = "ERROR reading BMAJ, BMIN, BPA: " + x.getMesg(); 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 ImageInfo& info, const IPosition& shape, const CoordinateSystem& csys) const { nchan = 0; if (csys.hasSpectralAxis()) { nchan = shape[csys.spectralAxisNumber()]; } AlwaysAssert (info.getBeamSet().nchan() == nchan || info.getBeamSet().nchan() == 1, AipsError); npol = 0; if (csys.hasPolarizationCoordinate()) { npol = shape[csys.polarizationAxisNumber()]; } AlwaysAssert (info.getBeamSet().nstokes() == npol || info.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()) { checkBeamShape (nchan1, npol1, *this, shapeThis, csysThis); } if (infoThat.hasBeam()) { checkBeamShape (nchan2, npol2, infoThat, 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; } 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); // Check if the beam shape matches the coordinates. void checkBeamShape (uInt& nchan, uInt& npol, const ImageInfo& info, const IPosition& shape, const CoordinateSystem& csys) const; // 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); // 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); //# 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-2.4.1/images/Images/ImageInterface.h000066400000000000000000000340241321422335000207620ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef IMAGES_IMAGEINTERFACE_H #define IMAGES_IMAGEINTERFACE_H //# Includes #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations template class LatticeIterInterface; template class Vector; 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; } 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; } // Get non-const access to the ImageInfo. ImageInfo& rwImageInfo() { return imageInfo_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. #ifdef AIPS_CXX11 extern template class ImageInterface; extern template class ImageInterface; #endif } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-2.4.1/images/Images/ImageInterface.tcc000066400000000000000000000251751321422335000213130ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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) { imageInfo_p = info; imageInfo_p.checkBeamSet (coords_p, shape(), name()); } 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-2.4.1/images/Images/ImageOpener.cc000066400000000000000000000213161321422335000204500ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ // #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 with the unknown as the default open function. SimpleOrderedMap ImageOpener::theirOpenFuncMap(&ImageOpener::unknownImageOpen); LatticeBase* ImageOpener::unknownImageOpen (const String&, const MaskSpecifier&) { return 0; } void ImageOpener::registerOpenImageFunction (ImageTypes type, OpenImageFunction* func) { theirOpenFuncMap.define (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 = Table::tableInfo (name); if (info.type() == TableInfo::type(TableInfo::PAGEDIMAGE)) { return AIPSPP; } else if (info.type() == TableInfo::type(TableInfo::COMPONENTLIST)) { TableDesc tableDesc; Table::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); // 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. return theirOpenFuncMap(type) (fileName, spec); } } //# NAMESPACE CASACORE - END casacore-2.4.1/images/Images/ImageOpener.h000066400000000000000000000126141321422335000203130ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #ifndef IMAGES_IMAGEOPENER_H #define IMAGES_IMAGEOPENER_H #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: // The default openImage function for an unknown image type. // It returns a null pointer. static LatticeBase* unknownImageOpen (const String& name, const MaskSpecifier&); // Mapping of the image type to an openImage function. static SimpleOrderedMap theirOpenFuncMap; }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/images/Images/ImageProxy.cc000066400000000000000000001300671321422335000203450ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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 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 CountedPtr& image) : itsLattice (image), itsImageFloat (0), itsImageDouble (0), itsImageComplex (0), itsImageDComplex (0), itsCoordSys (0), itsAttrHandler (0) { if (! itsLattice.null()) { setup(); } } ImageProxy::ImageProxy (const ImageProxy& that) : itsLattice (that.itsLattice), itsImageFloat (0), itsImageDouble (0), itsImageComplex (0), itsImageDComplex (0), itsCoordSys (0), itsAttrHandler (0) { if (! itsLattice.null()) { setup(); } } ImageProxy& ImageProxy::operator= (const ImageProxy& that) { if (this != &that) { close(); itsLattice = that.itsLattice; if (! itsLattice.null()) { 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 = CountedPtr(); itsImageFloat = 0; itsImageDouble = 0; itsImageComplex = 0; itsImageDComplex = 0; itsCoordSys = 0; itsAttrHandler = 0; } void ImageProxy::checkNull() const { if (itsLattice.null()) { 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 = lattice; setup(); } void ImageProxy::setup() { // Get raw pointer from the CountedPtr. LatticeBase* lattice = &(*itsLattice); 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-2.4.1/images/Images/ImageProxy.h000066400000000000000000000450001321422335000201770ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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 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 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 CountedPtr&); // 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 vector& images = 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. CountedPtr itsLattice; ImageInterface* itsImageFloat; ImageInterface* itsImageDouble; ImageInterface* itsImageComplex; ImageInterface* itsImageDComplex; const CoordinateSystem* itsCoordSys; ImageAttrHandler* itsAttrHandler; }; } // end namespace casacore #endif casacore-2.4.1/images/Images/ImageRegion.h000066400000000000000000000001671321422335000203060ustar00rootroot00000000000000#warning Deprecated in next release; use images/Regions/ImageRegion.h #include casacore-2.4.1/images/Images/ImageRegrid.h000066400000000000000000000410131321422335000202720ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #ifndef IMAGES_IMAGEREGRID_H #define IMAGES_IMAGEREGRID_H #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template class MaskedLattice; template class ImageInterface; template class Lattice; template class LatticeIterator; template class Vector; 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& 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& xOut, Float 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. #ifdef AIPS_CXX11 extern template class ImageRegrid; #endif } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-2.4.1/images/Images/ImageRegrid.tcc000066400000000000000000002223631321422335000206250ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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 Timer t0; uInt ii = 0; uInt 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; }; }; }; }; 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 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 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 InterpolateArray1D::InterpolationMethod method1D = InterpolateArray1D::linear; if (method==Interpolate2D::NEAREST) { method1D = InterpolateArray1D::nearestNeighbour; if (itsShowLevel>0) { cerr << "Method = nearest" << endl; } } else if (method==Interpolate2D::LINEAR) { method1D = InterpolateArray1D::linear; if (itsShowLevel>0) { cerr << "Method = linear" << endl; } } else if (method==Interpolate2D::CUBIC) { method1D = InterpolateArray1D::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::interpolate(outIter.rwVectorCursor(), outMaskIterPtr-> rwVectorCursor(), outX, inX, inY, inMask, method1D, goodIsTrue, extrapolate); } else { InterpolateArray1D::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::interpolate(outIter.rwVectorCursor(), outMaskIterPtr-> rwVectorCursor(), outX, inX, inY, (failed && inMask), method1D, goodIsTrue, extrapolate); } else { InterpolateArray1D::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& 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& outX, Float pixelScale) const { Float oX = -0.5 + (1.0/2/pixelScale); const uInt nLine = outX.nelements(); for (uInt i=0; i 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-2.4.1/images/Images/ImageStatistics.h000066400000000000000000000240661321422335000212210ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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; 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. #ifdef AIPS_CXX11 extern template class ImageStatistics; #endif } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-2.4.1/images/Images/ImageStatistics.tcc000066400000000000000000000752751321422335000215530ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 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. T* dummy(0); DataType type = whatType(dummy); 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(""); } 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(""); } 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(""); 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(""); 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); PtrHolder > beamAreaIter( gotBeamArea ? new ReadOnlyVectorIterator(beamArea) : 0 ); 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-2.4.1/images/Images/ImageSummary.h000066400000000000000000000165771321422335000205340ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #ifndef IMAGES_IMAGESUMMARY_H #define IMAGES_IMAGESUMMARY_H //# Includes #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations template class ImageInterface; template class Vector; 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-2.4.1/images/Images/ImageSummary.tcc000066400000000000000000000305421321422335000210420ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/images/Images/ImageUtilities.cc000066400000000000000000000234751321422335000212030ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ // #include #include #include #include #include #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 Vector; template class Quantum; template class MaskedArray; template class PtrHolder; 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 (PtrHolder >& image, const String& fileName); template static void openImage (ImageInterface*& image, const String& fileName); template static 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, PtrHolder >& 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-2.4.1/images/Images/ImageUtilities2.tcc000066400000000000000000000227371321422335000214510ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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, PtrHolder >& 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.set(new PagedImage(shape, cSys, outFile)); } ImageInterface* pOutImage = outImage.ptr(); // 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" ); T x = 0; if (lattPtr->dataType() != whatType(&x)) { 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( PtrHolder >& image, const String& fileName ) { ImageInterface* p = 0; ImageUtilities::openImage(p, fileName); image.set(p); } template SHARED_PTR > ImageUtilities::openImage (const String& fileName) { ImageInterface* p = 0; ImageUtilities::openImage(p, fileName); return SHARED_PTR > (p); } } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/images/Images/Image_tmpl.cc000066400000000000000000000037351321422335000203400ustar00rootroot00000000000000//# 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: aips2-request@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. #ifdef AIPS_CXX11 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; } #endif casacore-2.4.1/images/Images/Images_1.fig000066400000000000000000000072211321422335000200610ustar00rootroot00000000000000#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-2.4.1/images/Images/LELImageCoord.cc000066400000000000000000000161431321422335000206250ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/images/Images/LELImageCoord.h000066400000000000000000000120161321422335000204620ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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: CountedPtr 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-2.4.1/images/Images/MIRIADImage.cc000066400000000000000000000737341321422335000202000ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include #include #include #include #include #include #include #include #include #include #include #include #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 = 0; 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 = new TiledFileAccess(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 (AipsError x) { os << LogIO::SEVERE << "Error forming projection, maybe the " "wrong number of parameters\n(" << x.getMesg() << ")" << 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-2.4.1/images/Images/MIRIADImage.h000066400000000000000000000231651321422335000200330ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef IMAGES_MIRIADIMAGE_H #define IMAGES_MIRIADIMAGE_H //# Includes #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations template class Array; 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; CountedPtr 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-2.4.1/images/Images/MaskSpecifier.cc000066400000000000000000000026371321422335000210070ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# Includes #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# All functions are inlined. } //# NAMESPACE CASACORE - END casacore-2.4.1/images/Images/MaskSpecifier.h000066400000000000000000000063201321422335000206420ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/images/Images/PagedImage.h000066400000000000000000000437151321422335000201110ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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. #ifdef AIPS_CXX11 extern template class PagedImage; extern template class PagedImage; #endif } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-2.4.1/images/Images/PagedImage.tcc000066400000000000000000000543741321422335000204360ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/images/Images/PagedImage2.cc000066400000000000000000000035671321422335000203320ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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; Table::getLayout(desc, fileName); ColumnDesc cdesc = desc["map"]; retval = cdesc.dataType(); } catch (AipsError& x) { // Nothing } } return retval; } } //# NAMESPACE CASACORE - END casacore-2.4.1/images/Images/RebinImage.h000066400000000000000000000147461321422335000201320ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/images/Images/RebinImage.tcc000066400000000000000000000147771321422335000204600ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/images/Images/SubImage.h000066400000000000000000000235051321422335000176150ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef IMAGES_SUBIMAGE_H #define IMAGES_SUBIMAGE_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class IPosition; class LattRegionHolder; class Slicer; template class SubLattice; template class Array; 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. #ifdef AIPS_CXX11 extern template class SubImage; extern template class SubImage; #endif } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-2.4.1/images/Images/SubImage.tcc000066400000000000000000000271521321422335000201410ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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. #ifdef AIPS_CXX11 extern template class TempImage; extern template class TempImage; #endif } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-2.4.1/images/Images/TempImage.tcc000066400000000000000000000234701321422335000203140ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/images/Images/WCBox.h000066400000000000000000000001531321422335000170750ustar00rootroot00000000000000#warning Deprecated in next release; use images/Regions/WCBox.h #include casacore-2.4.1/images/Images/test/000077500000000000000000000000001321422335000167225ustar00rootroot00000000000000casacore-2.4.1/images/Images/test/CMakeLists.txt000066400000000000000000000034421321422335000214650ustar00rootroot00000000000000set (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) target_link_libraries (${test} casa_images) add_test (${test} ${CMAKE_SOURCE_DIR}/cmake/cmake_assay ./${test}) add_dependencies(check ${test}) endforeach (test) casacore-2.4.1/images/Images/test/dImageInterface.cc000066400000000000000000000047301321422335000222440ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/images/Images/test/dImageStatistics.cc000066400000000000000000000356021321422335000225000ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ // // // 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); 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); 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 (AipsError x) { cerr << "aipserror: error " << x.getMesg() << endl; return 1; } return 0; } casacore-2.4.1/images/Images/test/dImageSummary.cc000066400000000000000000000067501321422335000220050ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ // #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 (AipsError x) { cerr << "aipserror: error " << x.getMesg() << endl; return 1; } return 0; } casacore-2.4.1/images/Images/test/dPagedImage.cc000066400000000000000000000273201321422335000213640ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //#-------------------------------------------------------------------------- #include #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 (AipsError x) { cerr << "Caught Exception: " << x.getMesg() << 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-2.4.1/images/Images/test/dPagedImage.run000066400000000000000000000001251321422335000215750ustar00rootroot00000000000000#!/bin/sh echo "UNTESTED: dPagedImage requires retired ImageCoordinate class" exit 3 casacore-2.4.1/images/Images/test/decon_test.im/000077500000000000000000000000001321422335000214555ustar00rootroot00000000000000casacore-2.4.1/images/Images/test/decon_test.im/logtable/000077500000000000000000000000001321422335000232465ustar00rootroot00000000000000casacore-2.4.1/images/Images/test/decon_test.im/logtable/table.dat000066400000000000000000000025601321422335000250320ustar00rootroot00000000000000¾¾¾¾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-2.4.1/images/Images/test/imagetestimage.fits000066400000000000000000001111001321422335000225700ustar00rootroot00000000000000SIMPLE = 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-2.4.1/images/Images/test/input_image_processor_dont_replace_me000066400000000000000000000000001321422335000264330ustar00rootroot00000000000000casacore-2.4.1/images/Images/test/mexinputtest.fits000066400000000000000000001111001321422335000223540ustar00rootroot00000000000000SIMPLE = 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-2.4.1/images/Images/test/qualityimage.fits000066400000000000000000002145001321422335000223060ustar00rootroot00000000000000SIMPLE = 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-2.4.1/images/Images/test/tExtendImage.cc000066400000000000000000000207611321422335000216150ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { cerr << "Caught exception: " << x.getMesg() << endl; cout << "FAIL" << endl; return 1; } cout << "OK" << endl; return 0; } casacore-2.4.1/images/Images/test/tFITSErrorImage.cc000066400000000000000000000332551321422335000221470ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { cerr << "aipserror: error " << x.getMesg() << 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-2.4.1/images/Images/test/tFITSExtImage.cc000066400000000000000000000151711321422335000216130ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { cerr << "aipserror: error " << x.getMesg() << 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 (AipsError x) { cerr << "aipserror: error " << x.getMesg() << 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 (AipsError x) { cout << "aipserror: error " << x.getMesg() << 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-2.4.1/images/Images/test/tFITSImage.out000066400000000000000000000066331321422335000213570ustar00rootroot00000000000000SIMPLE = 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-2.4.1/images/Images/test/tFITSImage.run000066400000000000000000000003351321422335000213450ustar00rootroot00000000000000#!/bin/sh # Make output independent of date and version. $casa_checktool ./tFITSImage | fold -w 80 | sed 's/DATE =\(.*written\) /DATE =>>>\1<<>>\1<<<'/" casacore-2.4.1/images/Images/test/tFITSImgParser.cc000066400000000000000000000277201321422335000220040ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { cerr << "aipserror: error " << x.getMesg() << endl; return 1; } cout<< "[tFITSImgParser.cc] End of tFITSImgParser.cc."<< endl; cout << "ok " << endl; return 0; } casacore-2.4.1/images/Images/test/tFITSQualityImage.cc000066400000000000000000000571301321422335000225040ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { cerr << "aipserror: error " << x.getMesg() << 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-2.4.1/images/Images/test/tHDF5Image.cc000066400000000000000000000274301321422335000210540ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include #include #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 (AipsError x) { cerr << "Exception caught: " << x.getMesg() << endl; return 1; } return 0; } casacore-2.4.1/images/Images/test/tImageAttrHandler.cc000066400000000000000000000176211321422335000225770ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/images/Images/test/tImageAttrHandler.out000066400000000000000000000055721321422335000230230ustar00rootroot00000000000000 >>> 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-2.4.1/images/Images/test/tImageBeamSet.cc000066400000000000000000000645161321422335000217140ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 ); } } catch (const AipsError& x) { cout << x.getMesg() << endl; cout << "FAIL" << endl; return 1; } cout << "OK" << endl; return 0; } casacore-2.4.1/images/Images/test/tImageConcat.cc000066400000000000000000000706071321422335000216010ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError 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 (AipsError 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); } { cout << "*** single beams concat test with stokes, CAS-4423" << endl; CoordinateSystem csys = CoordinateUtil::defaultCoords4D(); TempImage 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 ); } } catch(const AipsError& x) { cerr << x.getMesg() << 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-2.4.1/images/Images/test/tImageEmpty.cc000066400000000000000000000050241321422335000214570ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/images/Images/test/tImageExpr.cc000066400000000000000000000113441321422335000213010ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: tImageConcat.cc 21469 2014-08-12 11:25:55Z gervandiepen $ #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.img1 + tImageExpr_tmp.imgexpr"); 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.img1" && ImageExprParse::getImageNames()[1] == "tImageExpr_tmp.imgexpr"); } } int main() { try { testExpr(); } catch (const AipsError& x) { cerr << x.getMesg() << endl; return 1; } cout << "OK" << endl; return 0; } casacore-2.4.1/images/Images/test/tImageExpr2.cc000066400000000000000000000037771321422335000213760ustar00rootroot00000000000000//# tImageExpr2.cc: Test program for boolean images with different shapes //# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include #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-2.4.1/images/Images/test/tImageExpr2Gram.cc000066400000000000000000000314631321422335000221760ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { cerr << "aipserror: error " << x.getMesg() << endl; foundError = True; } if (foundError) { return 1; } return 0; } casacore-2.4.1/images/Images/test/tImageExprGram.cc000066400000000000000000000546241321422335000221200ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { cout << x.getMesg() << endl; } try { LatticeExpr expr (ImageExprParse::command ("b/a1")); } catch (AipsError x) { cout << x.getMesg() << endl; } try { LatticeExpr expr (ImageExprParse::command ("a1/b")); } catch (AipsError x) { cout << x.getMesg() << endl; } try { LatticeExpr expr (ImageExprParse::command ("b/b*")); } catch (AipsError x) { cout << x.getMesg() << endl; } try { LatticeExpr expr (ImageExprParse::command ("min(b,b,b)")); } catch (AipsError x) { cout << x.getMesg() << 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) - C::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) + C::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 (AipsError x) { cerr << "aipserror: error " << x.getMesg() << 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-2.4.1/images/Images/test/tImageExprGram.out000066400000000000000000000031071321422335000223300ustar00rootroot00000000000000>>> /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-2.4.1/images/Images/test/tImageExprParse.cc000066400000000000000000000033301321422335000222700ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include #include #include #include int main() { try { Bool thrown = False; try { ImageExprParse::command("''"); } catch (const AipsError& x) { thrown = True; } AlwaysAssert(thrown, AipsError); cout<< "ok"<< endl; } catch (const AipsError& x) { cerr << "Exception caught: " << x.getMesg() << endl; return 1; } return 0; } casacore-2.4.1/images/Images/test/tImageExprParse_addDir.cc000066400000000000000000000041301321422335000235360ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { cerr << "Exception caught: " << x.getMesg() << endl; return 1; } return 0; } casacore-2.4.1/images/Images/test/tImageInfo.cc000066400000000000000000000215611321422335000212600ustar00rootroot00000000000000 //# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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 AipsError& x) { cout << x.getMesg() << 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 (AipsError x) { cout << "Exception thrown as expected: " << x.getMesg() << 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 (AipsError x) { cout << "Exception thrown as expected: " << x.getMesg() << 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 AipsError& x) { cout << "Caught error " << x.getMesg() << endl; return 1; } cout << "OK" << endl; return 0; } casacore-2.4.1/images/Images/test/tImageProxy.cc000066400000000000000000000035051321422335000215040ustar00rootroot00000000000000 //# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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 AipsError& x) { cout << "Caught error " << x.getMesg() << endl; return 1; } cout << "OK" << endl; return 0; } casacore-2.4.1/images/Images/test/tImageRegrid.cc000066400000000000000000000273511321422335000216040ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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 (AipsError x) { cerr << "aipserror: error " << x.getMesg() << endl; return 1; } return 0; } casacore-2.4.1/images/Images/test/tImageStatistics.cc000066400000000000000000000202171321422335000225140ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 AipsError& x) { cerr << "Exception caught: " << x.getMesg() << endl; return 1; } return 0; } casacore-2.4.1/images/Images/test/tImageStatistics2.cc000066400000000000000000000343011321422335000225750ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: $ #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); /* { CountedPtr > 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; CountedPtr > 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; CountedPtr > 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 AipsError& x) { cerr << "Exception caught: " << x.getMesg() << endl; return 1; } return 0; } casacore-2.4.1/images/Images/test/tImageTiledCache.in000066400000000000000000000000531321422335000223640ustar00rootroot000000000000002 512 16 0.5 16384 100 128 8 0.5 65536 100 casacore-2.4.1/images/Images/test/tImageUtilities.cc000066400000000000000000000177121321422335000223430ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include #include #include #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); { PtrHolder > im; ImageUtilities::openImage(im, name1); } { CountedPtr > im; im = ImageUtilities::openImage(name1); } { PtrHolder > 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 AipsError& x) { cout << "Unexpected exception: " << x.getMesg() << endl; return 1; } cout << "OK" << endl; return 0; } casacore-2.4.1/images/Images/test/tLELSpectralIndex.cc000066400000000000000000000143171321422335000225250ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { cerr << "Caught exception: " << x.getMesg() << endl; ok = False; } if (!ok) { cout << "FAIL" << endl; return 1; } cout << "OK" << endl; return 0; } casacore-2.4.1/images/Images/test/tMIRIADImage.cc000066400000000000000000000151161321422335000213310ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { cerr << "aipserror: error " << x.getMesg() << 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 // 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)); } Table::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 (AipsError& x) { // cout << "Caught error " << x.getMesg() << 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); } Table::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 (AipsError& x) { cout << "Exception thrown as expected: " << x.getMesg() << endl; } AlwaysAssert(ok, AipsError); info.setBeam(0, 0, maj, min, pa); try { temp.setImageInfo(info); ok = False; } catch (AipsError& 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 (AipsError& x) { cout << "Exception thrown as expected: " << x.getMesg() << 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 (AipsError& x) { cerr << "Exception caught: " << x.getMesg() << endl; return 1; } return 0; } casacore-2.4.1/images/Images/test/tPagedImage2.cc000066400000000000000000000160211321422335000214620ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { cerr << "Exception caught: " << x.getMesg() << endl; return 1; } return 0; } casacore-2.4.1/images/Images/test/tRebinImage.cc000066400000000000000000000132021321422335000214150ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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 (AipsError& x) { cout << "Exception thrown as expected: " << x.getMesg() << endl; exception = True; } AlwaysAssert(exception, AipsError); } delete pIm; } catch (AipsError x) { cerr << "aipserror: error " << x.getMesg() << endl; cout << "FAIL" << endl; return 1; } cout << "OK" << endl; return 0; } casacore-2.4.1/images/Images/test/tSubImage.cc000066400000000000000000000332671321422335000211240ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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(""); PtrHolder log(new LogIO()); PtrHolder outRegionMgr( ImageRegion::fromRecord( log.ptr(), 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 (AipsError x) { cerr << "Caught exception: " << x.getMesg() << endl; cout << "FAIL" << endl; return 1; } cout << "OK" << endl; return 0; } casacore-2.4.1/images/Images/test/tTempImage.cc000066400000000000000000000217131321422335000212710ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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) { MemoryIO membuf; CanonicalIO canio (&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.0); 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 (AipsError x) { cout << "Exception thrown as expected: " << x.getMesg() << endl; } info.setBeam(0, 0, maj, min, pa); try { temp.setImageInfo(info); } catch (AipsError 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 (AipsError& x) { cerr << x.getMesg() << endl; cout << "FAIL" << endl; return 1; } cout << "OK" << endl; return 0; } casacore-2.4.1/images/Images/test/test_image.im/000077500000000000000000000000001321422335000214475ustar00rootroot00000000000000casacore-2.4.1/images/Images/test/test_image.im/logtable/000077500000000000000000000000001321422335000232405ustar00rootroot00000000000000casacore-2.4.1/images/Images/test/test_image.im/logtable/table.dat000066400000000000000000000023561321422335000250270ustar00rootroot00000000000000¾¾¾¾ê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-2.4.1/images/Images/test/test_image.im/table.info000066400000000000000000000000311321422335000234050ustar00rootroot00000000000000Type = Image SubType = casacore-2.4.1/images/Images/test/test_image.im/table.lock000066400000000000000000000005051321422335000234100ustar00rootroot00000000000000=¾¾¾¾9syncBlockcasacore-2.4.1/images/Regions.h000066400000000000000000000036311321422335000163200ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/images/Regions/000077500000000000000000000000001321422335000161445ustar00rootroot00000000000000casacore-2.4.1/images/Regions/AipsIOReaderWriter.cc000066400000000000000000000107231321422335000221220ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/images/Regions/AipsIOReaderWriter.h000066400000000000000000000053571321422335000217730ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/images/Regions/ImageRegion.cc000066400000000000000000000222651321422335000206500ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/images/Regions/ImageRegion.h000066400000000000000000000161431321422335000205100ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/images/Regions/RFReaderWriter.cc000066400000000000000000000102611321422335000213020ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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(2); 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-2.4.1/images/Regions/RFReaderWriter.h000066400000000000000000000200661321422335000211500ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/images/Regions/RegionHandler.cc000066400000000000000000000074571321422335000212110ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/images/Regions/RegionHandler.h000066400000000000000000000146761321422335000210540ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef IMAGES_REGIONHANDLER_H #define IMAGES_REGIONHANDLER_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class Table; class ImageRegion; class LatticeBase; class LCPagedMask; class String; template class Vector; // // 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-2.4.1/images/Regions/RegionHandlerHDF5.cc000066400000000000000000000243331321422335000216100ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { error = True; msg = x.getMesg(); } 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-2.4.1/images/Regions/RegionHandlerHDF5.h000066400000000000000000000160121321422335000214450ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 CountedPtr& 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 CountedPtr& 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-2.4.1/images/Regions/RegionHandlerMemory.cc000066400000000000000000000217411321422335000223720ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN RegionHandlerMemory::RegionHandlerMemory() { itsMaps[0] = 0; itsMaps[1] = 0; itsMaps[0] = new SimpleOrderedMap (0); itsMaps[1] = new SimpleOrderedMap (0); } RegionHandlerMemory::RegionHandlerMemory (const RegionHandlerMemory& that) : RegionHandler(that) { itsMaps[0] = 0; itsMaps[1] = 0; itsMaps[0] = new SimpleOrderedMap (0); itsMaps[1] = new SimpleOrderedMap (0); operator= (that); } RegionHandlerMemory::~RegionHandlerMemory() { clear(); delete itsMaps[0]; delete itsMaps[1]; } RegionHandlerMemory& RegionHandlerMemory::operator= (const RegionHandlerMemory& that) { if (this != &that) { clear(); itsDefaultName = that.itsDefaultName; *(itsMaps[0]) = *(that.itsMaps[0]); *(itsMaps[1]) = *(that.itsMaps[1]); for (uInt i=0; indefined(); i++) { void*& valref = itsMaps[0]->getVal(i); valref = static_cast(valref)->clone(); } for (uInt i=0; indefined(); i++) { void*& valref = itsMaps[1]->getVal(i); valref = static_cast(valref)->clone(); } } return *this; } void RegionHandlerMemory::clear() { for (uInt i=0; indefined(); i++) { delete static_cast(itsMaps[0]->getVal(i)); } for (uInt i=0; indefined(); i++) { delete static_cast(itsMaps[1]->getVal(i)); } } 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]->remove (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]->define (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]->remove (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. itsMaps[oldGroupField]->rename (newName, oldName); // 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 (AipsError x) { error = True; msg = x.getMesg(); } 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]->remove (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]->ndefined(); } if (type != RegionHandler::Regions) { nmask = itsMaps[1]->ndefined(); } Vector names(nreg + nmask); uInt i; for (i=0; igetKey(i); } for (i=0; igetKey(i); } 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) { void* regPtr = (*itsMaps[groupField])(name); return static_cast(regPtr); } 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) { void* field = itsMaps[0]->isDefined (regionName); if (field != 0) { return 0; } } if (type != RegionHandler::Regions) { void* field = itsMaps[1]->isDefined (regionName); if (field != 0) { 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-2.4.1/images/Regions/RegionHandlerMemory.h000066400000000000000000000150471321422335000222360ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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; SimpleOrderedMap* itsMaps[2]; }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/images/Regions/RegionHandlerTable.cc000066400000000000000000000252761321422335000221600ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { error = True; msg = x.getMesg(); } 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-2.4.1/images/Regions/RegionHandlerTable.h000066400000000000000000000155101321422335000220100ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/images/Regions/RegionManager.cc000066400000000000000000000740751321422335000212060ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include #include #include #include #include #include #include #include #include #include #include #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() { itsLog= new LogIO(); itsCSys=0; } RegionManager::RegionManager(const CoordinateSystem& csys) : itsCSys(new CoordinateSystem(csys)) { itsLog= new LogIO(); //setcoordsys(csys); } RegionManager::~RegionManager() { if(itsLog !=0) delete itsLog; if(itsCSys != 0) delete itsCSys; } /************************************************************* ** 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= new CoordinateSystem(csys); } const CoordinateSystem& RegionManager::getcoordsys() const{ if (itsCSys == 0) { 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==0) { 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 !=0){ 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 == 0) { 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 == 0){ 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 == 0){ 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 == 0){ 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(AipsError x){ throw(AipsError("Could not write mask in image "+tabName+" because "+x.getMesg())); } 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-2.4.1/images/Regions/RegionManager.h000066400000000000000000000236451321422335000210450ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef IMAGES_REGIONMANAGER_H #define IMAGES_REGIONMANAGER_H #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; template class Vector; 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; CoordinateSystem* 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-2.4.1/images/Regions/WCBox.cc000066400000000000000000000545511321422335000174470ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/images/Regions/WCBox.h000066400000000000000000000311461321422335000173040ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# $Id$ #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-2.4.1/images/Regions/WCComplement.cc000066400000000000000000000062441321422335000210160ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/images/Regions/WCComplement.h000066400000000000000000000102641321422335000206550ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/images/Regions/WCCompound.cc000066400000000000000000000172541321422335000205020ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/images/Regions/WCConcatenation.cc000066400000000000000000000144611321422335000215000ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/images/Regions/WCConcatenation.h000066400000000000000000000136701321422335000213430ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/images/Regions/WCDifference.cc000066400000000000000000000063011321422335000207370ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/images/Regions/WCDifference.h000066400000000000000000000112311321422335000205770ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/images/Regions/WCEllipsoid.cc000066400000000000000000000377621321422335000206500ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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"), C::pi)); _theta.setUnit("rad"); if (_theta.getValue() < 0) { _theta + Quantity(C::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-2.4.1/images/Regions/WCExtension.cc000066400000000000000000000210521321422335000206610ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 extinx(ndext); std::vector tmpext(extOutOrd.begin(), extOutOrd.end()); GenSortIndirect::sort (extinx, &(tmpext[0]), ndext); for (uInt i=0; i 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-2.4.1/images/Regions/WCExtension.h000066400000000000000000000117301321422335000205250ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/images/Regions/WCIntersection.cc000066400000000000000000000073141321422335000213600ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/images/Regions/WCIntersection.h000066400000000000000000000121301321422335000212120ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/images/Regions/WCLELMask.cc000066400000000000000000000167071321422335000201500ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { throw AipsError (x.getMesg() + "\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-2.4.1/images/Regions/WCLELMask.h000066400000000000000000000136371321422335000200110ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# $Id$ #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-2.4.1/images/Regions/WCPolygon.cc000066400000000000000000000357441321422335000203510ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/images/Regions/WCRegion.cc000066400000000000000000000271621321422335000201400ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/images/Regions/WCRegion2.cc000066400000000000000000000074031321422335000202160ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/images/Regions/WCUnion.cc000066400000000000000000000067661321422335000200140ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/images/Regions/WCUnion.h000066400000000000000000000121611321422335000176400ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/images/Regions/test/000077500000000000000000000000001321422335000171235ustar00rootroot00000000000000casacore-2.4.1/images/Regions/test/CMakeLists.txt000066400000000000000000000010541321422335000216630ustar00rootroot00000000000000set (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) target_link_libraries (${test} casa_images) add_test (${test} ${CMAKE_SOURCE_DIR}/cmake/cmake_assay ./${test}) add_dependencies(check ${test}) endforeach (test) casacore-2.4.1/images/Regions/test/imregion.fits000066400000000000000000004103001321422335000216210ustar00rootroot00000000000000SIMPLE = 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-2.4.1/images/Regions/test/imregion_dironly.fits000066400000000000000000000207001321422335000233620ustar00rootroot00000000000000SIMPLE = 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-2.4.1/images/Regions/test/imregion_nospec.fits000066400000000000000000000341001321422335000231700ustar00rootroot00000000000000SIMPLE = 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-2.4.1/images/Regions/test/tImageRegion.cc000066400000000000000000000052621321422335000220110ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: HostInfoDarwin.h 21521 2014-12-10 08:06:42Z gervandiepen $ #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 AipsError& x) { cerr << "Caught exception: " << x.getMesg() << endl; cout << "FAIL" << endl; ret = 1; } Directory d(myname); if (d.exists()) { d.removeRecursive(False); } return ret; } casacore-2.4.1/images/Regions/test/tRegionHandler.cc000066400000000000000000000146731321422335000223520ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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; } CountedPtr theHDF5File; const CountedPtr& 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 = new HDF5File ("tRegionHandler_tmp.hdf5", ByteIO::New); RegionHandlerHDF5 reghdf5 (getHDF5File, 0); doIt (reghdf5); } } catch (AipsError x) { cerr << "Unexpected exception: " << x.getMesg() << endl; return 1; } cout << "ok" << endl; return 0; } casacore-2.4.1/images/Regions/test/tRegionManager.cc000066400000000000000000000601521321422335000223400ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { cerr << "Unexpected exception: " << x.getMesg() << endl; return 1; } cout << "ok" << endl; return 0; } casacore-2.4.1/images/Regions/test/tWCBox.cc000066400000000000000000000403761321422335000206120ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { cerr << "aipserror: error " << x.getMesg() << 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-2.4.1/images/Regions/test/tWCEllipsoid.cc000066400000000000000000000302621321422335000217770ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { cout << "Caught as expected " << x.getMesg() << endl; } radius[2] = Quantity(50, "MHz"); try { WCEllipsoid(center, radius, pixelAxes, csys); AlwaysAssert(False, AipsError); } catch (AipsError x) { cout << "Caught as expected " << x.getMesg() << endl; } center[2] = Quantity(1415, "GHz"); pixelAxes[2] = 3; try { WCEllipsoid(center, radius, pixelAxes, csys); AlwaysAssert(False, AipsError); } catch (AipsError x) { cout << "Caught as expected " << x.getMesg() << endl; } pixelAxes = IPosition(3, 0, 0, 1); try { WCEllipsoid(center, radius, pixelAxes, csys); AlwaysAssert(False, AipsError); } catch (AipsError x) { cout << "Caught as expected " << x.getMesg() << endl; } pixelAxes = IPosition(3, 0, 1, 2); center.resize(2, True); try { WCEllipsoid(center, radius, pixelAxes, csys); AlwaysAssert(False, AipsError); } catch (AipsError x) { cout << "Caught as expected " << x.getMesg() << 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(AipsError x) { cout << "Caught as expected " << x.getMesg() << 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(AipsError x) { cout << "Caught as expected " << x.getMesg() << 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(AipsError x) { cout << "Caught as expected " << x.getMesg() << 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(AipsError x) { cout << "Caught as expected " << x.getMesg() << 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 (AipsError x) { cout << "Caught exception: " << x.getMesg() << endl; return 1; } cout << "OK" << endl; return 0; } casacore-2.4.1/images/Regions/test/tWCExtension.cc000066400000000000000000000273131321422335000220320ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { cout << "Caught exception: " << x.getMesg() << endl; cout << "FAIL" << endl; return 1; } cout << "OK" << endl; return 0; } casacore-2.4.1/images/Regions/test/tWCExtension.out000066400000000000000000000014741321422335000222540ustar00rootroot00000000000000[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-2.4.1/images/Regions/test/tWCLELMask.cc000066400000000000000000000156551321422335000213140ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { cout << "Expected exception: " << x.getMesg() << 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 (AipsError x) { cout << "Expected exception: " << x.getMesg() << 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 (AipsError x) { cerr << "Caught exception: " << x.getMesg() << endl; return 1; } cout << "OK" << endl; return 0; } casacore-2.4.1/images/Regions/test/tWCLELMask.out000066400000000000000000000003051321422335000215200ustar00rootroot00000000000000Expected 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-2.4.1/images/Regions/test/tWCUnion.cc000066400000000000000000000102351321422335000211410ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { cerr << "Caught exception: " << x.getMesg() << endl; cout << "FAIL" << endl; return 1; } cout << "OK" << endl; return 0; } casacore-2.4.1/images/apps/000077500000000000000000000000001321422335000155015ustar00rootroot00000000000000casacore-2.4.1/images/apps/CMakeLists.txt000066400000000000000000000004231321422335000202400ustar00rootroot00000000000000foreach(prog image2fits imagecalc imageregrid imageslice) add_executable (${prog} ${prog}.cc) target_link_libraries (${prog} casa_images ${CASACORE_ARCH_LIBS}) install(TARGETS ${prog} DESTINATION bin) endforeach(prog image2fits imagecalc imageregrid imageslice) casacore-2.4.1/images/apps/image2fits.cc000066400000000000000000000072401321422335000200450ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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-2.4.1/images/apps/imagecalc.cc000066400000000000000000000070421321422335000177200ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: #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 (AipsError x) { cout << x.getMesg() << endl; return 1; } return 0; } casacore-2.4.1/images/apps/imageregrid.cc000066400000000000000000000173521321422335000202770ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: #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.getMesg() << endl; } } casacore-2.4.1/images/apps/imageslice.cc000066400000000000000000000102251321422335000201120ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: #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.getMesg() << endl; } } casacore-2.4.1/images/images.dox000066400000000000000000000036111321422335000165200ustar00rootroot00000000000000//# 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: aips2-request@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-2.4.1/lattices/000077500000000000000000000000001321422335000151015ustar00rootroot00000000000000casacore-2.4.1/lattices/CMakeLists.txt000066400000000000000000000142101321422335000176370ustar00rootroot00000000000000# # 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/LatticeFFT.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 ) target_link_libraries ( casa_lattices casa_tables casa_scimath ${CASACORE_ARCH_LIBS} ) install (TARGETS casa_lattices 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 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/LatticeAddNoise.h 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/LatticeFit.h LatticeMath/LatticeFractile.h LatticeMath/LatticeFractile.tcc LatticeMath/LatticeHistProgress.h LatticeMath/LatticeHistSpecialize.h 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/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/LELBinary2.h 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/LELFunction2.h 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/LELUnary2.h 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 LatticeMath.h Lattices.h LEL.h LRegions.h 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-2.4.1/lattices/LEL.h000066400000000000000000000055601321422335000156740ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: Lattices.h 21521 2014-12-10 08:06:42Z gervandiepen $ #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-2.4.1/lattices/LEL/000077500000000000000000000000001321422335000155155ustar00rootroot00000000000000casacore-2.4.1/lattices/LEL/LELArray.h000066400000000000000000000103031321422335000172760ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/LEL/LELArray.tcc000066400000000000000000000035731321422335000176330ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/LEL/LELArrayBase.cc000066400000000000000000000130351321422335000202340ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/LEL/LELArrayBase.h000066400000000000000000000105771321422335000201060ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/LEL/LELAttribute.cc000066400000000000000000000150511321422335000203260ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/LEL/LELAttribute.h000066400000000000000000000115531321422335000201730ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/LEL/LELBinary.h000066400000000000000000000262031321422335000174520ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 CountedPtr >& pLeftExpr, const CountedPtr >& 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; CountedPtr > pLeftExpr_p; CountedPtr > 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 CountedPtr >& pLeftExpr, const CountedPtr >& 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; CountedPtr > pLeftExpr_p; CountedPtr > 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 CountedPtr >& pLeftExpr, const CountedPtr >& 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; CountedPtr > pLeftExpr_p; CountedPtr > pRightExpr_p; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-2.4.1/lattices/LEL/LELBinary.tcc000066400000000000000000000320711321422335000177740ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 CountedPtr >& pLeftExpr, const CountedPtr >& 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 CountedPtr >& pLeftExpr, const CountedPtr >& 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-2.4.1/lattices/LEL/LELBinary2.cc000066400000000000000000000213401321422335000176670ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN LELBinaryBool::LELBinaryBool(const LELBinaryEnums::Operation op, const CountedPtr >& pLeftExpr, const CountedPtr >& 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-2.4.1/lattices/LEL/LELBinary2.h000066400000000000000000000000001321422335000175170ustar00rootroot00000000000000casacore-2.4.1/lattices/LEL/LELBinaryEnums.h000066400000000000000000000052251321422335000204630ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/LEL/LELCondition.h000066400000000000000000000104431321422335000201530ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 CountedPtr >& expr, const CountedPtr >& 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: CountedPtr > pExpr_p; CountedPtr > pCond_p; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-2.4.1/lattices/LEL/LELCondition.tcc000066400000000000000000000103421321422335000204730ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef LATTICES_LELCONDITION_TCC #define LATTICES_LELCONDITION_TCC #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template LELCondition::LELCondition (const CountedPtr >& expr, const CountedPtr >& 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-2.4.1/lattices/LEL/LELConvert.h000066400000000000000000000106241321422335000176460ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 CountedPtr >& 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: CountedPtr > pExpr_p; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-2.4.1/lattices/LEL/LELConvert.tcc000066400000000000000000000066661321422335000202030ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef LATTICES_LELCONVERT_TCC #define LATTICES_LELCONVERT_TCC #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template LELConvert::LELConvert(const CountedPtr >& 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-2.4.1/lattices/LEL/LELCoordinates.cc000066400000000000000000000053331321422335000206370ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (!coords_p.null(), AipsError); return *coords_p; } // Does it have coordinates ? Bool LELCoordinates::hasCoordinates() const { if (coords_p.null()) { return False; } return coords_p->hasCoordinates(); } // Check if the coordinates of this and that conform. Int LELCoordinates::compare (const LELCoordinates& that) const { if (coords_p.null() || that.coords_p.null()) { return 9; } return coords_p->compare (*(that.coords_p)); } } //# NAMESPACE CASACORE - END casacore-2.4.1/lattices/LEL/LELCoordinates.h000066400000000000000000000146101321422335000204770ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 CountedPtr) 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 CountedPtr) 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 CountedPtrs 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.null(); } // 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. CountedPtr coords_p; }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/lattices/LEL/LELFunction.h000066400000000000000000000655551321422335000200300ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 CountedPtr >& 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; CountedPtr > 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 CountedPtr >& 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; CountedPtr > 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-2.4.1/lattices/LEL/LELFunction.tcc000066400000000000000000000662161321422335000203450ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 CountedPtr >& 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 CountedPtr >& 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.null()) { 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(static_cast(0)); argType[2] = whatType(static_cast(0)); 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(static_cast(0)); argType[1] = whatType(static_cast(0)); 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(), Double(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-2.4.1/lattices/LEL/LELInterface.h000066400000000000000000000223731321422335000201320ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (CountedPtr >& 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-2.4.1/lattices/LEL/LELInterface.tcc000066400000000000000000000064671321422335000204620ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (CountedPtr >& 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 = new LELUnaryConst (tmp.value()); } else { isInvalidScalar = True; } } // If the value is an invalid scalar expression, replace by scalar // with false mask. if (isInvalidScalar) { expr = new LELUnaryConst(); } 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-2.4.1/lattices/LEL/LELLattCoord.cc000066400000000000000000000076631321422335000202700ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/LEL/LELLattCoord.h000066400000000000000000000101301321422335000201110ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/LEL/LELLattCoordBase.cc000066400000000000000000000026451321422335000210560ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include namespace casacore { //# NAMESPACE CASACORE - BEGIN LELLattCoordBase::~LELLattCoordBase() {} } //# NAMESPACE CASACORE - END casacore-2.4.1/lattices/LEL/LELLattCoordBase.h000066400000000000000000000071101321422335000207100ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef LATTICES_LELLATTCOORDBASE_H #define LATTICES_LELLATTCOORDBASE_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class LELImageCoord; class IPosition; template class Vector; // // 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-2.4.1/lattices/LEL/LELLattice.h000066400000000000000000000106051321422335000176120ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/LEL/LELLattice.tcc000066400000000000000000000110271321422335000201330ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/LEL/LELRegion.cc000066400000000000000000000126511321422335000176110ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/LEL/LELRegion.h000066400000000000000000000141611321422335000174510ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/LEL/LELScalar.h000066400000000000000000000046141321422335000174350ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/LEL/LELScalar.tcc000066400000000000000000000030601321422335000177510ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/LEL/LELSpectralIndex.h000066400000000000000000000075051321422335000207770ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/LEL/LELSpectralIndex.tcc000066400000000000000000000165731321422335000213260ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/LEL/LELUnary.h000066400000000000000000000232661321422335000173320ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 CountedPtr >& 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; CountedPtr > 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 CountedPtr >& 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; CountedPtr > 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-2.4.1/lattices/LEL/LELUnary.tcc000066400000000000000000000110241321422335000176410ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 CountedPtr >& 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-2.4.1/lattices/LEL/LELUnary2.cc000066400000000000000000000065561321422335000175550ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN LELUnaryBool::LELUnaryBool(const LELUnaryEnums::Operation op, const CountedPtr >& 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-2.4.1/lattices/LEL/LELUnary2.h000066400000000000000000000000001321422335000173710ustar00rootroot00000000000000casacore-2.4.1/lattices/LEL/LELUnaryEnums.h000066400000000000000000000047101321422335000203330ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/LEL/LatticeExpr.h000066400000000000000000000221701321422335000201140ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef LATTICES_LATTICEEXPR_H #define LATTICES_LATTICEEXPR_H //# Includes #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations template class Array; 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-2.4.1/lattices/LEL/LatticeExpr.tcc000066400000000000000000000174501321422335000204430ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (static_cast(0)); 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-2.4.1/lattices/LEL/LatticeExprNode.cc000066400000000000000000002070441321422335000210650ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include #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 CountedPtr >& pExpr) : donePrepare_p (False), dtype_p (TpFloat), isInvalid_p (True), pAttr_p (&pExpr->getAttribute()), pExprFloat_p (pExpr) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: constructor (CountedPtr>&); pExpr_p.nrefs() = " << pExprFloat_p.nrefs() << endl; #endif } LatticeExprNode::LatticeExprNode(const CountedPtr >& pExpr) : donePrepare_p (False), dtype_p (TpDouble), isInvalid_p (True), pAttr_p (&pExpr->getAttribute()), pExprDouble_p (pExpr) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: constructor (CountedPtr>&); pExpr_p.nrefs() = " << pExprDouble_p.nrefs() << endl; #endif } LatticeExprNode::LatticeExprNode (const CountedPtr >& pExpr) : donePrepare_p (False), dtype_p (TpComplex), isInvalid_p (True), pAttr_p (&pExpr->getAttribute()), pExprComplex_p (pExpr) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: constructor (CountedPtr>&); pExpr_p.nrefs() = " << pExprComplex_p.nrefs() << endl; #endif } LatticeExprNode::LatticeExprNode (const CountedPtr >& pExpr) : donePrepare_p (False), dtype_p (TpDComplex), isInvalid_p (True), pAttr_p (&pExpr->getAttribute()), pExprDComplex_p (pExpr) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: constructor (CountedPtr>&); pExpr_p.nrefs() = " << pExprDComplex_p.nrefs() << endl; #endif } LatticeExprNode::LatticeExprNode(const CountedPtr >& pExpr) : donePrepare_p (False), dtype_p (TpBool), isInvalid_p (True), pAttr_p (&pExpr->getAttribute()), pExprBool_p (pExpr) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: constructor (CountedPtr>&); 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 (new LELUnaryConst (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 (new LELUnaryConst (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 (new LELUnaryConst (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 (new LELUnaryConst (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 (new LELUnaryConst (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 (new LELUnaryConst (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 (new LELUnaryConst (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 (new LELUnaryConst (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 (new LELUnaryConst (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 (new LELLattice (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 (new LELLattice (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 (new LELLattice (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 (new LELLattice (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 (new LELLattice (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 (new LELLattice (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 (new LELLattice (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 (new LELLattice (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 (new LELLattice (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 (new LELLattice (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 (new LELRegion (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 (new LELRegion (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 (new LELRegion (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(); LELFunctionFloat* ptr = new LELFunctionFloat(LELFunctionEnums::SIGN, arg); return ptr; } 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 new LELFunctionComplex (LELFunctionEnums::COMPLEX, arg); } arg[0] = left.makeDouble(); arg[1] = right.makeDouble(); LELFunctionDComplex* ptr = new LELFunctionDComplex (LELFunctionEnums::COMPLEX, arg); return ptr; } 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 new LELFunctionFloat (LELFunctionEnums::FRACTILE1D, arg); case TpDouble: return new LELFunctionDouble (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 new LELFunctionFloat (LELFunctionEnums::FRACTILERANGE1D, arg); case TpDouble: return new LELFunctionDouble (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 new LELFunctionFloat (LELFunctionEnums::FRACTILERANGE1D, arg); case TpDouble: return new LELFunctionDouble (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/C::pi) * expr; break; case TpDouble: return Double(90.0/C::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 new LELSpectralIndex (arg); case TpDouble: arg[0] = left.makeDouble(); arg[1] = right.makeDouble(); return new LELSpectralIndex (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 new LELUnaryBool(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 new LELCondition (pExprBool_p, cond.pExprBool_p); case TpFloat: return new LELCondition (pExprFloat_p, cond.pExprBool_p); case TpDouble: return new LELCondition (pExprDouble_p, cond.pExprBool_p); case TpComplex: return new LELCondition (pExprComplex_p, cond.pExprBool_p); case TpDComplex: return new LELCondition (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; LELFunctionBool* ptr = new LELFunctionBool(LELFunctionEnums::INDEXIN, arg); return ptr; } LatticeExprNode all (const LatticeExprNode& expr) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: 1d function all" << endl; #endif Block arg(1, toBool(expr)); LELFunctionBool* ptr = new LELFunctionBool(LELFunctionEnums::ALL, arg); return ptr; } LatticeExprNode any (const LatticeExprNode& expr) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: 1d function any" << endl; #endif Block arg(1, toBool(expr)); LELFunctionBool* ptr = new LELFunctionBool(LELFunctionEnums::ANY, arg); return ptr; } LatticeExprNode ntrue (const LatticeExprNode& expr) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: 1d function ntrue" << endl; #endif Block arg(1, toBool(expr)); LELFunctionDouble* ptr = new LELFunctionDouble (LELFunctionEnums::NTRUE, arg); return ptr; } LatticeExprNode nfalse (const LatticeExprNode& expr) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: 1d function nfalse" << endl; #endif Block arg(1, toBool(expr)); LELFunctionDouble* ptr = new LELFunctionDouble (LELFunctionEnums::NFALSE, arg); return ptr; } 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); } LELFunctionDouble* ptr = new LELFunctionDouble (LELFunctionEnums::NELEM, arg); return ptr; } LatticeExprNode ndim (const LatticeExprNode& expr) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: 1d function ndim" << endl; #endif Block arg(1, expr); LELFunctionFloat* ptr = new LELFunctionFloat(LELFunctionEnums::NDIM, arg); return ptr; } 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; LELFunctionFloat* ptr = new LELFunctionFloat(LELFunctionEnums::LENGTH, arg); return ptr; } LatticeExprNode isNaN (const LatticeExprNode& expr) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: 1d function isNaN" << endl; #endif Block arg(1, expr); LELFunctionBool* ptr = new LELFunctionBool(LELFunctionEnums::ISNAN, arg); return ptr; } 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); } LELFunctionBool* ptr = new LELFunctionBool(LELFunctionEnums::MASK, arg); return ptr; } 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 new LELFunctionBool (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 new LELFunctionND(LELFunctionEnums::IIF, arg); case TpDouble: arg[1] = arg1.makeDouble(); arg[2] = arg2.makeDouble(); return new LELFunctionND(LELFunctionEnums::IIF, arg); case TpComplex: arg[1] = arg1.makeComplex(); arg[2] = arg2.makeComplex(); return new LELFunctionND(LELFunctionEnums::IIF, arg); case TpDComplex: arg[1] = arg1.makeDComplex(); arg[2] = arg2.makeDComplex(); return new LELFunctionND(LELFunctionEnums::IIF, arg); case TpBool: arg[1] = arg1.makeBool(); arg[2] = arg2.makeBool(); return new LELFunctionND(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 new LELFunctionND(LELFunctionEnums::REPLACE, arg); case TpDouble: arg[0] = arg1.makeDouble(); arg[1] = arg2.makeDouble(); return new LELFunctionND(LELFunctionEnums::REPLACE, arg); case TpComplex: arg[0] = arg1.makeComplex(); arg[1] = arg2.makeComplex(); return new LELFunctionND(LELFunctionEnums::REPLACE, arg); case TpDComplex: arg[0] = arg1.makeDComplex(); arg[1] = arg2.makeDComplex(); return new LELFunctionND(LELFunctionEnums::REPLACE, arg); case TpBool: arg[0] = arg1.makeBool(); arg[1] = arg2.makeBool(); return new LELFunctionND(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 new LELUnary (oper, expr.pExprFloat_p); case TpDouble: return new LELUnary (oper, expr.pExprDouble_p); case TpComplex: return new LELUnary (oper, expr.pExprComplex_p); case TpDComplex: return new LELUnary (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 new LELFunction1D (func, expr.pExprFloat_p); case TpDouble: return new LELFunction1D (func, expr.pExprDouble_p); case TpComplex: return new LELFunction1D (func, expr.pExprComplex_p); case TpDComplex: return new LELFunction1D (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 new LELFunctionReal1D (func, expr.pExprFloat_p); case TpDouble: return new LELFunctionReal1D (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 new LELFunctionComplex(func, arg); case TpDComplex: return new LELFunctionDComplex(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 new LELFunctionFloat (func, arg); case TpDouble: case TpDComplex: return new LELFunctionDouble (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 new LELFunctionFloat (func, arg); case TpDouble: arg[0] = left.makeDouble(); arg[1] = right.makeDouble(); return new LELFunctionDouble (func, arg); case TpComplex: arg[0] = left.makeComplex(); arg[1] = right.makeComplex(); return new LELFunctionComplex (func, arg); case TpDComplex: arg[0] = left.makeDComplex(); arg[1] = right.makeDComplex(); return new LELFunctionDComplex (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 new LELBinary (oper, expr0.pExprFloat_p, expr1.pExprFloat_p); case TpDouble: return new LELBinary (oper, expr0.pExprDouble_p, expr1.pExprDouble_p); case TpComplex: return new LELBinary (oper, expr0.pExprComplex_p, expr1.pExprComplex_p); default: return new LELBinary (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 new LELBinaryBool (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 new LELBinaryCmp (oper, expr0.pExprFloat_p, expr1.pExprFloat_p); case TpDouble: return new LELBinaryCmp (oper, expr0.pExprDouble_p, expr1.pExprDouble_p); case TpComplex: return new LELBinaryCmp (oper, expr0.pExprComplex_p, expr1.pExprComplex_p); case TpDComplex: return new LELBinaryCmp (oper, expr0.pExprDComplex_p, expr1.pExprDComplex_p); default: return new LELBinaryBool (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 new LELConvert (pExprDouble_p); default: throw (AipsError ("LatticeExprNode::makeFloat - " "conversion to Float not possible")); } } CountedPtr > LatticeExprNode::makeDouble() const { switch (dataType()) { case TpFloat: return new LELConvert (pExprFloat_p); case TpDouble: return pExprDouble_p; default: throw (AipsError ("LatticeExprNode::makeDouble - " "conversion to Double not possible")); } } CountedPtr > LatticeExprNode::makeComplex() const { switch (dataType()) { case TpFloat: return new LELConvert (pExprFloat_p); case TpDouble: return new LELConvert (pExprDouble_p); case TpComplex: return pExprComplex_p; case TpDComplex: return new LELConvert (pExprDComplex_p); default: throw (AipsError ("LatticeExprNode::makeComplex - " "conversion to Complex not possible")); } } CountedPtr > LatticeExprNode::makeDComplex() const { switch (dataType()) { case TpFloat: return new LELConvert (pExprFloat_p); case TpDouble: return new LELConvert (pExprDouble_p); case TpComplex: return new LELConvert (pExprComplex_p); case TpDComplex: return pExprDComplex_p; default: throw (AipsError ("LatticeExprNode::makeDComplex - " "conversion to DComplex not possible")); } } CountedPtr > LatticeExprNode::makeBool() const { if (dataType() != TpBool) { throw (AipsError ("LatticeExprNode::makeBool - " "conversion to Bool not possible")); } if (isRegion()) { return new LELRegionAsBool ((const LELRegion&)(*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-2.4.1/lattices/LEL/LatticeExprNode.h000066400000000000000000001077501321422335000207320ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef LATTICES_LATTICEEXPRNODE_H #define LATTICES_LATTICEEXPRNODE_H //# Includes #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 Array; 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. // CountedPtr > makeFloat() const; CountedPtr > makeDouble() const; CountedPtr > makeComplex() const; CountedPtr > makeDComplex() const; CountedPtr > 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 Counted pointer. // Ideally this function is private, but alas it is needed in LELFunction1D, // operator==, and more (too many to make them friend). // LatticeExprNode(const CountedPtr >& expr); LatticeExprNode(const CountedPtr >& expr); LatticeExprNode(const CountedPtr >& expr); LatticeExprNode(const CountedPtr >& expr); LatticeExprNode(const CountedPtr >& 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; CountedPtr > pExprFloat_p; CountedPtr > pExprDouble_p; CountedPtr > pExprComplex_p; CountedPtr > pExprDComplex_p; CountedPtr > 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-2.4.1/lattices/LEL/test/000077500000000000000000000000001321422335000164745ustar00rootroot00000000000000casacore-2.4.1/lattices/LEL/test/CMakeLists.txt000066400000000000000000000005711321422335000212370ustar00rootroot00000000000000set (tests tLEL tLELAttribute tLELMedian tLatticeExpr tLatticeExpr2 tLatticeExpr3 tLatticeExprNode tLatticeExpr2Node tLatticeExpr3Node ) foreach (test ${tests}) add_executable (${test} ${test}.cc) target_link_libraries (${test} casa_lattices) add_test (${test} ${CMAKE_SOURCE_DIR}/cmake/cmake_assay ./${test}) add_dependencies(check ${test}) endforeach (test) casacore-2.4.1/lattices/LEL/test/tLEL.cc000066400000000000000000002511241321422335000176100ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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; { CountedPtr > pExpr = new LELLattice(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; CountedPtr > pExpr = new LELLattice(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; CountedPtr > pExpr = new LELLattice(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; CountedPtr > pExpr = new LELLattice(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; CountedPtr > pExpr = new LELLattice(aB); LELUnaryBool expr(LELUnaryEnums::NOT, pExpr); if (!checkBool(expr, (!aBVal), String("LELUnaryBool"), shape, False, suppress)) ok = False; } } // //************************************************************************ // // LELBinary // { cout << endl << "LELBinary" << endl; CountedPtr > pExprLeft = new LELLattice(bF); CountedPtr > pExprRight = new LELLattice(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; CountedPtr > pExprLeft = new LELLattice(bD); CountedPtr > pExprRight = new LELLattice(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; CountedPtr > pExprLeft = new LELLattice(bC); CountedPtr > pExprRight = new LELLattice(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; CountedPtr > pExprLeft = new LELLattice(bDC); CountedPtr > pExprRight = new LELLattice(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; CountedPtr > pExprLeft = new LELLattice(bF); CountedPtr > pExprRight = new LELLattice(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; CountedPtr > pExprLeft = new LELLattice(bD); CountedPtr > pExprRight = new LELLattice(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; CountedPtr > pExprLeft = new LELLattice(bC); CountedPtr > pExprRight = new LELLattice(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; CountedPtr > pExprLeft = new LELLattice(bDC); CountedPtr > pExprRight = new LELLattice(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; CountedPtr > pExprLeft = new LELLattice(bB); CountedPtr > pExprRight = new LELLattice(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; CountedPtr > pExpr = new LELLattice(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; CountedPtr > pExpr = new LELLattice(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; CountedPtr > pExpr = new LELLattice(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; CountedPtr > pExpr = new LELLattice(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; CountedPtr > pExpr = new LELLattice(bF); CountedPtr > pExpra = new LELLattice(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; CountedPtr > pExpr = new LELLattice(bD); CountedPtr > pExpra = new LELLattice(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; CountedPtr > pExpr = new LELLattice(bD); LELConvert expr(pExpr); FResult = Float(bDVal); if (!checkFloat (expr, FResult, String("LELConvert"), shape, False, suppress)) ok = False; } { cout << "LELConvert " << endl; CountedPtr > pExpr = new LELLattice(bF); LELConvert expr(pExpr); DResult = Double(bFVal); if (!checkDouble(expr, DResult, String("LELConvert"), shape, False, suppress)) ok = False; } { cout << "LELConvert " << endl; CountedPtr > pExpr = new LELLattice(bDC); LELConvert expr(pExpr); CResult = Complex(bDCVal.real(), bDCVal.imag()); if (!checkComplex(expr, CResult, String("LELConvert"), shape, False, suppress)) ok = False; } { cout << "LELConvert " << endl; CountedPtr > pExpr = new LELLattice(bC); LELConvert expr(pExpr); DCResult = bCVal; if (!checkDComplex(expr, DCResult, String("LELConvert"), shape, False, suppress)) ok = False; } { cout << "LELConvert " << endl; CountedPtr > pExpr = new LELLattice(bF); LELConvert expr(pExpr); CResult = Complex(bFVal,0.0); if (!checkComplex(expr, CResult, String("LELConvert"), shape, False, suppress)) ok = False; } { cout << "LELConvert " << endl; CountedPtr > pExpr = new LELLattice(bD); LELConvert expr(pExpr); CResult = Complex(bDVal,0.0); if (!checkComplex(expr, CResult, String("LELConvert"), shape, False, suppress)) ok = False; } { cout << "LELConvert " << endl; CountedPtr > pExpr = new LELLattice(bF); LELConvert expr(pExpr); DCResult = DComplex(bFVal,0.0); if (!checkDComplex(expr, DCResult, String("LELConvert"), shape, False, suppress)) ok = False; } { cout << "LELConvert " << endl; CountedPtr > pExpr = new LELLattice(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 (AipsError x) { cerr << "aipserror: error " << x.getMesg() << 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 (AipsError x) { if (!suppress) cout << " Caught expected exception; message is: " << x.getMesg() << 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 (AipsError x) { if (!suppress) cout << " Caught expected exception; message is: " << x.getMesg() << 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 (AipsError x) { if (!suppress) cout << " Caught expected exception; message is: " << x.getMesg() << 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 (AipsError x) { if (!suppress) cout << " Caught expected exception; message is: " << x.getMesg() << 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 (AipsError x) { if (!suppress) cout << " Caught expected exception; message is: " << x.getMesg() << 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 (AipsError x) { if (!suppress) cout << " Caught expected exception; message is: " << x.getMesg() << 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 (AipsError x) { if (!suppress) cout << " Caught expected exception; message is: " << x.getMesg() << 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 (AipsError x) { if (!suppress) cout << " Caught expected exception; message is: " << x.getMesg() << 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 (AipsError x) { if (!suppress) cout << " Caught expected exception; message is: " << x.getMesg() << 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 (AipsError x) { if (!suppress) cout << " Caught expected exception; message is: " << x.getMesg() << 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-2.4.1/lattices/LEL/test/tLEL.out000066400000000000000000000472131321422335000200340ustar00rootroot00000000000000>>> /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-2.4.1/lattices/LEL/test/tLELAttribute.cc000066400000000000000000000142721321422335000214750ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError& 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-2.4.1/lattices/LEL/test/tLELMedian.cc000066400000000000000000000156321321422335000207300ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 AipsError& x) { cout << "Unexpected exception: " << x.getMesg() << endl; return 1; } return 0; } casacore-2.4.1/lattices/LEL/test/tLELMedian.out000066400000000000000000000017561321422335000211540ustar00rootroot00000000000000>>> /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-2.4.1/lattices/LEL/test/tLatticeExpr.cc000066400000000000000000000444741321422335000214300ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { cerr << "aipserror: error " << x.getMesg() << 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 (AipsError x) { if (!supress) cout << " Caught expected exception; message is: " << x.getMesg() << endl; } try { expr.putSlice(outArr, origin); } catch (AipsError x) { if (!supress) cout << " Caught expected exception; message is: " << x.getMesg() << 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 (AipsError x) { if (!supress) cout << " Caught expected exception; message is: " << x.getMesg() << endl; } try { expr.putSlice(outArr, origin); } catch (AipsError x) { if (!supress) cout << " Caught expected exception; message is: " << x.getMesg() << 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 (AipsError x) { if (!supress) cout << " Caught expected exception; message is: " << x.getMesg() << endl; } try { expr.putSlice(outArr, origin); } catch (AipsError x) { if (!supress) cout << " Caught expected exception; message is: " << x.getMesg() << 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 (AipsError x) { if (!supress) cout << " Caught expected exception; message is: " << x.getMesg() << endl; } try { expr.putSlice(outArr, origin); } catch (AipsError x) { if (!supress) cout << " Caught expected exception; message is: " << x.getMesg() << 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 (AipsError x) { if (!supress) cout << " Caught expected exception; message is: " << x.getMesg() << endl; } try { expr.putSlice(outArr, origin); } catch (AipsError x) { if (!supress) cout << " Caught expected exception; message is: " << x.getMesg() << 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-2.4.1/lattices/LEL/test/tLatticeExpr2.cc000066400000000000000000000237541321422335000215100ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { cerr << "aipserror: error " << x.getMesg() << endl; return 1; } return 0; } casacore-2.4.1/lattices/LEL/test/tLatticeExpr2Node.cc000066400000000000000000000672431321422335000223170ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { cout << "Caught exception: " << x.getMesg() << endl; ok = False; } if (ok) { cout << "OK" << endl; return 0; } return 1; } casacore-2.4.1/lattices/LEL/test/tLatticeExpr3.cc000066400000000000000000000305261321422335000215040ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { cerr << "aipserror: error " << x.getMesg() << endl; return 1; } return 0; } casacore-2.4.1/lattices/LEL/test/tLatticeExpr3Node.cc000066400000000000000000000123771321422335000223160ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { cout << "Caught exception: " << x.getMesg() << endl; ok = False; } if (ok) { cout << "OK" << endl; return 0; } return 1; } casacore-2.4.1/lattices/LEL/test/tLatticeExprNode.cc000066400000000000000000003564271321422335000222420ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include #include #include #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(CountedPtr >&) " << endl; { cout << " Float" << endl; CountedPtr > pExpr = new LELUnaryConst(bFVal); LatticeExprNode expr(pExpr); LatticeExprNode expr2(bFVal); if (!compareScalarFloat (expr, expr2, bFVal, shape)) ok = False; } { cout << " Double" << endl; CountedPtr > pExpr = new LELUnaryConst(bDVal); LatticeExprNode expr(pExpr); LatticeExprNode expr2(bDVal); if (!compareScalarDouble (expr, expr2, bDVal, shape)) ok = False; } { cout << " Complex" << endl; CountedPtr > pExpr = new LELUnaryConst(bCVal); LatticeExprNode expr(pExpr); LatticeExprNode expr2(bCVal); if (!compareScalarComplex (expr, expr2, bCVal, shape)) ok = False; } { cout << " DComplex" << endl; CountedPtr > pExpr = new LELUnaryConst(bDCVal); LatticeExprNode expr(pExpr); LatticeExprNode expr2(bDCVal); if (!compareScalarDComplex (expr, expr2, bDCVal, shape)) ok = False; } { cout << " Bool" << endl; CountedPtr > pExpr = new LELUnaryConst(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/C::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/C::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/C::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/C::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 (AipsError x) { cerr << "aipserror: error " << x.getMesg() << endl; ok = False; } if (!ok) { return 1; } cout << endl << "ok" << endl; return 0; } casacore-2.4.1/lattices/LEL/test/tLatticeExprNode.out000066400000000000000000000410371321422335000224500ustar00rootroot00000000000000>>> tLatticeExprNode: Version <<< Constant contructors, get*, eval, shape, dataType, isScalar LatticeExprNode (constant T) Int Float Double Complex DComplex Bool LELInterface constructors LatticeExprNode(CountedPtr >&) 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(CountedPtr >&) 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(CountedPtr >&) 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-2.4.1/lattices/LRegions.h000066400000000000000000000073421321422335000170020ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: Lattices.h 21521 2014-12-10 08:06:42Z gervandiepen $ #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-2.4.1/lattices/LRegions/000077500000000000000000000000001321422335000166235ustar00rootroot00000000000000casacore-2.4.1/lattices/LRegions/FITSMask.cc000066400000000000000000000150301321422335000205120ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/LRegions/FITSMask.h000066400000000000000000000133071321422335000203610ustar00rootroot00000000000000 //# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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-2.4.1/lattices/LRegions/LCBox.cc000066400000000000000000000211551321422335000201050ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/LRegions/LCBox.h000066400000000000000000000117051321422335000177470ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/LRegions/LCComplement.cc000066400000000000000000000100521321422335000214520ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/LRegions/LCComplement.h000066400000000000000000000075471321422335000213330ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/LRegions/LCConcatenation.cc000066400000000000000000000234711321422335000221450ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/LRegions/LCConcatenation.h000066400000000000000000000146311321422335000220050ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/LRegions/LCDifference.cc000066400000000000000000000113341321422335000214050ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/LRegions/LCDifference.h000066400000000000000000000100341321422335000212430ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/LRegions/LCEllipsoid.cc000066400000000000000000000372211321422335000213020ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/LRegions/LCExtension.cc000066400000000000000000000241331321422335000213300ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/LRegions/LCExtension.h000066400000000000000000000126221321422335000211720ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/LRegions/LCHDF5Mask.cc000066400000000000000000000126201321422335000206540ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN LCHDF5Mask::LCHDF5Mask() {} LCHDF5Mask::LCHDF5Mask (const TiledShape& latticeShape, const CountedPtr& 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 CountedPtr& 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-2.4.1/lattices/LRegions/LCHDF5Mask.h000066400000000000000000000113601321422335000205160ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 CountedPtr& file, const String& maskName); LCHDF5Mask (const TiledShape& maskShape, const LCBox& box, const CountedPtr& 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-2.4.1/lattices/LRegions/LCIntersection.cc000066400000000000000000000140041321422335000220160ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/LRegions/LCIntersection.h000066400000000000000000000106551321422335000216700ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/LRegions/LCLELMask.cc000066400000000000000000000066341321422335000206120ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/LRegions/LCLELMask.h000066400000000000000000000115311321422335000204440ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# $Id$ #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-2.4.1/lattices/LRegions/LCMask.cc000066400000000000000000000135261321422335000202530ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/LRegions/LCMask.h000066400000000000000000000155451321422335000201200ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/LRegions/LCPagedMask.cc000066400000000000000000000165021321422335000212110ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/LRegions/LCPagedMask.h000066400000000000000000000154121321422335000210520ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/LRegions/LCPixelSet.cc000066400000000000000000000071331321422335000211120ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/LRegions/LCPixelSet.h000066400000000000000000000066371321422335000207640ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/LRegions/LCPolygon.cc000066400000000000000000000304001321422335000207750ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (near(vt, v)) { res = static_cast(v+0.1); } else { res = static_cast(v+1); } return std::max (res, 0); } // 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 (near(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 (!near (itsX[nrp-1], itsX[0]) || !near(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 && near(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]) || near(yf, ptrY[i]) || near(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 (near (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 namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations template class Matrix; // // 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); 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-2.4.1/lattices/LRegions/LCRegion.cc000066400000000000000000000141561321422335000206030ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/LRegions/LCRegion.h000066400000000000000000000216201321422335000204370ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/LRegions/LCRegion2.cc000066400000000000000000000101041321422335000206520ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/LRegions/LCRegionFixed.cc000066400000000000000000000041261321422335000215570ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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); } } //# NAMESPACE CASACORE - END casacore-2.4.1/lattices/LRegions/LCRegionFixed.h000066400000000000000000000067331321422335000214270ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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; 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-2.4.1/lattices/LRegions/LCRegionMulti.cc000066400000000000000000000247261321422335000216220ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/LRegions/LCRegionMulti.h000066400000000000000000000132501321422335000214520ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/LRegions/LCRegionSingle.cc000066400000000000000000000127051321422335000217430ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/LRegions/LCRegionSingle.h000066400000000000000000000114461321422335000216060ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/LRegions/LCSlicer.cc000066400000000000000000000415741321422335000206050ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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.nelements()) { itsInc = 1; fillFlags (fractional, absRel, blc.nelements(), trc.nelements(), itsInc.nelements()); 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.nelements(), trc.nelements(), inc.nelements()); 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.nelements()); inc = 1; fillFlags (fractional, absRel, blc.nelements(), trc.nelements(), inc.nelements()); fillFromDouble (blc, trc, inc); } LCSlicer::LCSlicer (const Vector& blc, const Vector& trc, const Vector& inc, Bool fractional, RegionType::AbsRelType absRel) { fillFlags (fractional, absRel, blc.nelements(), trc.nelements(), inc.nelements()); 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.nelements()); inc = 1; fillFlags (False, absRel, blc.nelements(), trc.nelements(), inc.nelements()); fillFromIPosition (blc, trc, inc); } LCSlicer::LCSlicer (const IPosition& blc, const IPosition& trc, const IPosition& inc, RegionType::AbsRelType absRel) { fillFlags (False, absRel, blc.nelements(), trc.nelements(), inc.nelements()); fillFromIPosition (blc, trc, inc); } LCSlicer::LCSlicer (const IPosition& blc, const IPosition& trc, const IPosition& inc, const Vector& absRelBlc, const Vector& absRelTrc) : itsFracBlc (blc.nelements()), itsFracTrc (trc.nelements()), itsFracInc (inc.nelements()), 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.nelements(); 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.nelements() != other.itsBlc.nelements() || 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.nelements()); for (i=0; i vec (nr); for (uInt i=0; i& referencePixel, const IPosition& newLatticeShape) const { uInt nr = referencePixel.nelements(); Vector vec (nr); for (uInt i=0; i& referencePixel, const IPosition& newLatticeShape) const { uInt i; if (referencePixel.nelements() != newLatticeShape.nelements()) { throw (AipsError ("LCSlicer::makeComplete - " "referencePixel and newLatticeShape vectors " "do not have same length")); } uInt ndreg = ndim(); uInt ndout = newLatticeShape.nelements(); 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. 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 or if MimicSource+1. if (oneRel) { for (i=0; isetComment (rec.asString ("comment")); } return regPtr; } } //# NAMESPACE CASACORE - END casacore-2.4.1/lattices/LRegions/LCSlicer.h000066400000000000000000000224461321422335000204440ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/LRegions/LCStretch.cc000066400000000000000000000213731321422335000207730ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/LRegions/LCStretch.h000066400000000000000000000124621321422335000206340ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/LRegions/LCUnion.cc000066400000000000000000000126311321422335000204440ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/LRegions/LCUnion.h000066400000000000000000000106661321422335000203140ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/LRegions/LattRegionHolder.cc000066400000000000000000000132041321422335000223400ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/LRegions/LattRegionHolder.h000066400000000000000000000133441321422335000222070ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/LRegions/LatticeRegion.cc000066400000000000000000000171521321422335000216710ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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-2.4.1/lattices/LRegions/RegionType.cc000066400000000000000000000033501321422335000212200ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/LRegions/RegionType.h000066400000000000000000000052761321422335000210730ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/LRegions/test/000077500000000000000000000000001321422335000176025ustar00rootroot00000000000000casacore-2.4.1/lattices/LRegions/test/CMakeLists.txt000066400000000000000000000007161321422335000223460ustar00rootroot00000000000000set (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) target_link_libraries (${test} casa_lattices) add_test (${test} ${CMAKE_SOURCE_DIR}/cmake/cmake_assay ./${test}) add_dependencies(check ${test}) endforeach (test) casacore-2.4.1/lattices/LRegions/test/tLCComplement.cc000066400000000000000000000112171321422335000226210ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { cout << "Caught exception: " << x.getMesg() << endl; return 1; } cout << "OK" << endl; return 0; } casacore-2.4.1/lattices/LRegions/test/tLCComplement.out000066400000000000000000000054121321422335000230430ustar00rootroot000000000000001 [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-2.4.1/lattices/LRegions/test/tLCConcatenation.cc000066400000000000000000000145051321422335000233060ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { cout << "Caught exception: " << x.getMesg() << endl; return 1; } cout << "OK" << endl; return 0; } casacore-2.4.1/lattices/LRegions/test/tLCConcatenation.out000066400000000000000000000071101321422335000235220ustar00rootroot000000000000001 [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-2.4.1/lattices/LRegions/test/tLCDifference.cc000066400000000000000000000117741321422335000225600ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { cout << "Caught exception: " << x.getMesg() << endl; return 1; } cout << "OK" << endl; return 0; } casacore-2.4.1/lattices/LRegions/test/tLCDifference.out000066400000000000000000000021701321422335000227700ustar00rootroot000000000000001 [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-2.4.1/lattices/LRegions/test/tLCEllipsoid.cc000066400000000000000000000201351321422335000224410ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 + C::pi; LCEllipsoid ellipse2( xcenter, ycenter, major, minor, theta2, latticeShape ); AlwaysAssert(ellipse == ellipse2, AipsError); near(ellipse.theta(), ellipse2.theta()); Float theta3 = theta - C::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 = C::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 = C::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 AipsError& 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 = C::pi/36; thrown = False; try { LCEllipsoid ellipse( xcenter, ycenter, major, minor, theta, latticeShape ); } catch (const AipsError& 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 = C::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 AipsError& x) { cout << "Caught exception: " << x.getMesg() << endl; return 1; } cout << "OK" << endl; return 0; } casacore-2.4.1/lattices/LRegions/test/tLCEllipsoid.out000066400000000000000000000536331321422335000226740ustar00rootroot000000000000000 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 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-2.4.1/lattices/LRegions/test/tLCExtension.cc000066400000000000000000000132141321422335000224710ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { cout << x.getMesg() << 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 (AipsError x) { cout << "Caught exception: " << x.getMesg() << endl; return 1; } cout << "OK" << endl; return 0; } casacore-2.4.1/lattices/LRegions/test/tLCExtension.out000066400000000000000000000115551321422335000227210ustar00rootroot00000000000000Axis 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-2.4.1/lattices/LRegions/test/tLCIntersection.cc000066400000000000000000000116141321422335000231650ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { cout << x.getMesg() << endl; } } catch (AipsError x) { cout << "Caught exception: " << x.getMesg() << endl; return 1; } cout << "OK" << endl; return 0; } casacore-2.4.1/lattices/LRegions/test/tLCIntersection.out000066400000000000000000000003161321422335000234040ustar00rootroot000000000000001 [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-2.4.1/lattices/LRegions/test/tLCLELMask.cc000066400000000000000000000073541321422335000217550ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { cerr << "Caught exception: " << x.getMesg() << endl; return 1; } cout << "OK" << endl; return 0; } casacore-2.4.1/lattices/LRegions/test/tLCMask.cc000066400000000000000000000103021321422335000214030ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { cerr << "Caught exception: " << x.getMesg() << endl; return 1; } cout << "OK" << endl; return 0; } casacore-2.4.1/lattices/LRegions/test/tLCMask.out000066400000000000000000000002351321422335000216310ustar00rootroot000000000000001Axis 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-2.4.1/lattices/LRegions/test/tLCPagedMask.cc000066400000000000000000000111751321422335000223550ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { cerr << "Caught exception: " << x.getMesg() << endl; return 1; } cout << "OK" << endl; return 0; } casacore-2.4.1/lattices/LRegions/test/tLCPagedMask.out000066400000000000000000000002351321422335000225720ustar00rootroot000000000000001Axis 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-2.4.1/lattices/LRegions/test/tLCPixelSet.cc000066400000000000000000000135721321422335000222610ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { cerr << "Caught exception: " << x.getMesg() << endl; cout << "FAIL" << endl; return 1; } cout << "OK" << endl; return 0; } casacore-2.4.1/lattices/LRegions/test/tLCPixelSet.out000066400000000000000000000002351321422335000224730ustar00rootroot000000000000001Axis 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-2.4.1/lattices/LRegions/test/tLCPolygon.cc000066400000000000000000000212521321422335000221450ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError& 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 (AipsError& 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; } } catch (AipsError x) { cout << "Caught exception: " << x.getMesg() << endl; return 1; } cout << "OK" << endl; return 0; } casacore-2.4.1/lattices/LRegions/test/tLCPolygon.out000066400000000000000000000155241321422335000223740ustar00rootroot00000000000000[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-2.4.1/lattices/LRegions/test/tLCPolygon2.cc000066400000000000000000000102501321422335000222230ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { cout << "Caught exception: " << x.getMesg() << endl; return 1; } cout << "OK" << endl; return 0; } casacore-2.4.1/lattices/LRegions/test/tLCRegion.cc000066400000000000000000000151571321422335000217500ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { cout << "Caught exception: " << x.getMesg() << endl; return 1; } cout << "OK" << endl; return 0; } casacore-2.4.1/lattices/LRegions/test/tLCRegion.out000066400000000000000000000036041321422335000221640ustar00rootroot000000000000000 [] [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-2.4.1/lattices/LRegions/test/tLCSlicer.cc000066400000000000000000000201731321422335000217400ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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); } } int main() { try { doIt(); } catch (AipsError x) { cout << "Caught exception: " << x.getMesg() << endl; return 1; } cout << "OK" << endl; return 0; } casacore-2.4.1/lattices/LRegions/test/tLCSlicer.out000066400000000000000000000004641321422335000221630ustar00rootroot00000000000000[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-2.4.1/lattices/LRegions/test/tLCStretch.cc000066400000000000000000000145751321422335000221440ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { cout << x.getMesg() << 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 (AipsError x) { cout << x.getMesg() << 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 (AipsError x) { cout << "Caught exception: " << x.getMesg() << endl; return 1; } cout << "OK" << endl; return 0; } casacore-2.4.1/lattices/LRegions/test/tLCStretch.out000066400000000000000000000117571321422335000223650ustar00rootroot00000000000000Axis 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-2.4.1/lattices/LRegions/test/tLCUnion.cc000066400000000000000000000113731321422335000216110ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { cout << "Caught exception: " << x.getMesg() << endl; return 1; } cout << "OK" << endl; return 0; } casacore-2.4.1/lattices/LRegions/test/tLCUnion.out000066400000000000000000000020501321422335000220230ustar00rootroot000000000000001 [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-2.4.1/lattices/LRegions/test/tLatticeRegion.cc000066400000000000000000000057251321422335000230370ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { cout << "Caught exception: " << x.getMesg() << endl; return 1; } cout << "OK" << endl; return 0; } casacore-2.4.1/lattices/LRegions/test/tLatticeRegion.out000066400000000000000000000011501321422335000232450ustar00rootroot00000000000000circle: 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-2.4.1/lattices/LatticeMath.h000066400000000000000000000037561321422335000174640ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/LatticeMath/000077500000000000000000000000001321422335000173005ustar00rootroot00000000000000casacore-2.4.1/lattices/LatticeMath/CLIPNearest2D.h000066400000000000000000000071021321422335000217100ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/LatticeMath/CLIPNearest2D.tcc000066400000000000000000000135021321422335000222330ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/LatticeMath/CLInterpolator2D.h000066400000000000000000000121731321422335000225440ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/LatticeMath/CLInterpolator2D.tcc000066400000000000000000000047121321422335000230660ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/LatticeMath/Fit2D.cc000066400000000000000000000747371321422335000205410ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 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::fit(const MaskedLattice& data, const Lattice& sigma) { if (!itsValid) { itsErrorMessage = "No models have been set - use function addModel"; return Fit2D::NOMODELS; } // // Get data // Array pixels = data.get(True); IPosition shape = pixels.shape(); if (shape.nelements() !=2) { itsLogger << "Fit2D::fit - Region must be 2-dimensional" << LogIO::EXCEPTION; } Array mask = data.getMask(True); // // Do fit // if (sigma.ndim()==0) { Array sigma2; return fit(pixels, mask, sigma2); } else { Array sigma2 = sigma.get(True); return fit(pixels, mask, sigma2); } } Fit2D::ErrorTypes Fit2D::fit(const Lattice& data, const Lattice& sigma) { if (!itsValid) { itsErrorMessage = "No models have been set - use function addModel"; return Fit2D::NOMODELS; } // Array 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 { Array sigma2 = sigma.get(True); return fit(pixels, mask, sigma2); } } 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); } 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); } 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) PtrHolder > > sumFunction(itsFunction.clone()); for (uInt i=0; i& 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) + C::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 Fit2D::estimate(Fit2D::Types type, const MaskedLattice& data) { if (data.shape().nelements() !=2) { itsLogger << "Fit2D::estimate - Lattice must be 2-dimensional" << LogIO::EXCEPTION; } Array pixels = data.get(True); Array mask = data.getMask(True); return estimate(type, pixels, mask); } Vector Fit2D::estimate(Fit2D::Types type, const Lattice& data) { if (data.shape().nelements() !=2) { itsLogger << "Fit2D::estimate - Lattice must be 2-dimensional" << LogIO::EXCEPTION; } Array pixels = data.get(True); Array mask(pixels.shape(),True); return estimate(type, pixels, mask); } 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); } 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; IPosition 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); Float 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 // Float 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; Float t, fac, SP; P = XP = YP = XYP = XXP = YYP = 0.0; SP = 0.0; // IPosition pos(2); Float 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)); // Float 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; } // Private functions 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 // { IPosition shape = pixels.shape(); uInt 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 // Bool hasMask = True; if (mask.nelements()==0) hasMask = False; Double minVal(0); Double maxVal(0); if (hasMask) { Bool deleteIt1, deleteIt2; const Bool* p1 = mask.getStorage(deleteIt1); const Float* p2 = pixels.getStorage(deleteIt2); for (uInt i=0; i locX(nPoints); Vector locY(nPoints); IPosition loc(2); // itsNumberPoints = 0; for (Int j=0; j #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template class Array; template class Matrix; template class Vector; 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. // Vector estimate(Fit2D::Types type, const MaskedLattice& data); Vector estimate(Fit2D::Types type, const Lattice& data); Vector estimate(Fit2D::Types type, const Array& data); 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 // Fit2D::ErrorTypes fit(const MaskedLattice& data, const Lattice& sigma); Fit2D::ErrorTypes fit(const Lattice& data, const Lattice& sigma); Fit2D::ErrorTypes fit(const Array& data, const Array& sigma); 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. // 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 (Float value, const Vector& range, Int includeIt) const; 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 (Float 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 #endif casacore-2.4.1/lattices/LatticeMath/LattStatsProgress.cc000066400000000000000000000044631321422335000232660ustar00rootroot00000000000000//# LattStatsProgress.cc: //# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ // #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN LattStatsProgress::~LattStatsProgress() {} void LattStatsProgress::operator++(Int) { ++_currentStep; _meter->update (_currentStep); } void LattStatsProgress::initDerived() // // Initialize meter // { // 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 // _meter = new ProgressMeter(0.0, Double(expectedNsteps()), String("Generate Storage Image"), String("Accumulation Iterations"), String(""), String(""), True, max(1,Int(expectedNsteps()/20))); } void LattStatsProgress::nstepsDone (uInt nsteps) { _currentStep = nsteps; _meter->update (_currentStep); } void LattStatsProgress::done() { _meter = NULL; } } //# NAMESPACE CASACORE - END casacore-2.4.1/lattices/LatticeMath/LattStatsProgress.h000066400000000000000000000063041321422335000231240ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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: CountedPtr _meter; uInt _currentStep; }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/lattices/LatticeMath/LattStatsSpecialize.cc000066400000000000000000000402331321422335000235450ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ // #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN void LattStatsSpecialize::accumulate ( Double& nPts, Double& sum, Double& mean, Double& nvariance,Double& variance, Double& sumSq, Float& dataMin, Float& dataMax, Int& minPos, Int& maxPos, Bool& minMaxInit, const Bool fixedMinMax, const Float datum, const uInt& pos, const Float useIt ) { if (useIt < 0) { return; } nPts += 1.0; sum += datum; sumSq += datum*datum; Double prevMean = mean; mean += (datum - prevMean)/nPts; nvariance += (datum - prevMean)*(datum - mean); variance = (nPts == 1) ? 0 : nvariance/(nPts-1); // If fixedMinMax, then the min and max will always // be given by the inclusion range that the user specified. // This will be set outside of here. We have no // more work to do if so. if (fixedMinMax) { return; } // Set min and max if (minMaxInit) { dataMin = datum; dataMax = datum; minPos = pos; maxPos = pos; minMaxInit = False; } else { if (datum < dataMin) { dataMin = datum; minPos = pos; } if (datum > dataMax) { dataMax = datum; maxPos = pos; } } } void LattStatsSpecialize::accumulate ( DComplex& nPts, DComplex& sum, DComplex& mean, DComplex& nvariance, DComplex& variance, DComplex& sumSq, Complex& dataMin, Complex& dataMax, const Int&, const Int&, Bool& minMaxInit, const Bool fixedMinMax, const Complex datum, const uInt&, const Complex useIt ) { // note the position of the maximum and minimum were not determined in the previous // version of this function and so are not here. It is not clear of what value the // min and max calculated in this method are, but I preserved that from the // previous incarnation -- dmehring 2011mar01 const Float rd = real(datum); const Float id = imag(datum); const Double rm = real(mean); const Double im = imag(mean); if (real(useIt) > 0.5) { nPts += Complex(1.0, 0.0); sum += DComplex(rd, 0.0); sumSq += DComplex(rd*rd, 0.0); Double prevMean = rm; Double rn = real(nPts); mean += DComplex((rd - prevMean)/rn, 0); nvariance += DComplex((rd - prevMean)*(rd - real(mean)), 0); variance = (rn <= 1) ? 0 : DComplex(real(nvariance)/(rn-1), imag(variance)); } if (imag(useIt) > 0.5) { nPts += Complex(0.0, 1.0); sum += DComplex(0.0, id); sumSq += DComplex(0.0, id*id); Double prevMean = im; Double in = imag(nPts); mean += DComplex(0, (id - prevMean)/in); nvariance += DComplex(0, (id - prevMean)*(id - imag(mean))); variance = (in <= 1) ? 0 : DComplex(real(variance), imag(nvariance)/(in-1)); } // If fixedMinMax, then the min and max will always // be given by the inclusion range that the user specified. // This will be set outside of here. We have no // more work to do if so. if (fixedMinMax) return; // Set min and max if (minMaxInit) { dataMin = datum; dataMax = datum; minMaxInit = False; } else { if (real(useIt) > 0.5) { if (rd < real(dataMin)) { dataMin = Complex(rd, dataMin.imag()); } if (rd > real(dataMax)) { dataMax = Complex(rd, dataMax.imag()); } } if (imag(useIt) > 0.5) { if (id < imag(dataMin)) { dataMin = Complex(dataMin.real(), id); } if (id > imag(dataMax)) { dataMax = Complex(dataMax.real(), id); } } } } 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) // // 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 { 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 { errorMessage = String("Too many elements for argument include"); return False; } // noExclude = True; if (exclude.nelements() == 0) { ; } else if (exclude.nelements() == 1) { range.resize(2); range(0) = -abs(exclude(0)); range(1) = abs(exclude(0)); noExclude = False; } else if (exclude.nelements() == 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; } 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-2.4.1/lattices/LatticeMath/LattStatsSpecialize.h000066400000000000000000000132231321422335000234060ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef LATTICES_LATTSTATSSPECIALIZE_H #define LATTICES_LATTSTATSSPECIALIZE_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template class Vector; template class Array; template class Lattice; template class MaskedLattice; class LatticeExprNode; class String; class IPosition; // // // // // // // // // // // // // // // // // // // // class LattStatsSpecialize { public: // !!! WARNING !!! // BOTH accumulate() METHODS ARE DEPRECATED AND NO LONGER USED BY CASACORE NOR // CASA. THESE METHODS WILL BE REMOVED IN THE NEAR FUTURE. PLEASE MODIFY EXISTING // CODE WHICH USES THEM. CURRENT STATISTIC CLASSES MAY BE FOUND IN scimath/Mathematics. // in this version we maintain a running mean and variance to avoid catastrophic round-off // issues that can happen in some cases, CAS-2226. Removing old versions in which these // quantities were not accumulated - dmehring 2011mar01 static void accumulate ( Double& nPts, Double& sum, Double& mean, Double& nvariance, Double& variance, Double& sumSq, Float& dataMin, Float& dataMax, Int& minPos, Int& maxPos, Bool& minMaxInit, const Bool fixedMinMax, const Float datum, const uInt& pos, const Float useIt ); static void accumulate (DComplex& nPts, DComplex& sum, DComplex& mean, DComplex& nvariance,DComplex& variance, DComplex& sumSq, Complex& dataMin, Complex& dataMax, const Int& minPos, const Int& maxPos, Bool& minMaxInit, const Bool fixedMinMax, const Complex datum, const uInt& pos, const Complex useIt); 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); // 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 #endif casacore-2.4.1/lattices/LatticeMath/LatticeAddNoise.cc000066400000000000000000000120051321422335000226010ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: LatticeAddNoise.cc 21549 2015-01-28 10:01:12Z gervandiepen $ #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(); } void LatticeAddNoise::add (MaskedLattice& lattice) { if (!itsNoise) { LogIO os(LogOrigin("LatticeAddNoise", "add", WHERE)); os << "You have not yet called function 'set'" << LogIO::EXCEPTION; } // LatticeIterator it(lattice); for (it.reset(); !it.atEnd(); it++) { addNoiseToArray(it.rwCursor()); } } void LatticeAddNoise::add (MaskedLattice& lattice) { if (!itsNoise) { LogIO os(LogOrigin("LatticeAddNoise", "add", WHERE)); os << "You have not yet called function 'set'" << LogIO::EXCEPTION; } // LatticeIterator it(lattice); for (it.reset(); !it.atEnd(); it++) { addNoiseToArray(it.rwCursor()); } } void LatticeAddNoise::add (Lattice& lattice) { SubLattice ml(lattice, True); add(ml); } void LatticeAddNoise::add (Lattice& lattice) { SubLattice ml(lattice, True); add(ml); } // Private void LatticeAddNoise::addNoiseToArray (Array& data) { Bool deleteIt; Float* p = data.getStorage(deleteIt); for (uInt i=0; i& data) { Bool deleteIt; Complex* p = data.getStorage(deleteIt); Float rr, ii; for (uInt i=0; icheckParameters(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-2.4.1/lattices/LatticeMath/LatticeAddNoise.h000066400000000000000000000100531321422335000224440ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id: LatticeAddNoise.h 21549 2015-01-28 10:01:12Z gervandiepen $ #ifndef LATTICES_LATTICEADDNOISE_H #define LATTICES_LATTICEADDNOISE_H //# Includes #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, 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. // void add (MaskedLattice& lattice); void add (MaskedLattice& lattice); void add (Lattice& lattice); void add (Lattice& 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); // // Make noise generator void makeDistribution (); }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/lattices/LatticeMath/LatticeApply.h000066400000000000000000000252441321422335000220530ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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); }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-2.4.1/lattices/LatticeMath/LatticeApply.tcc000066400000000000000000000540731321422335000223770ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: LatticeApply.tcc 21563 2015-02-16 07:05:15Z gervandiepen $ #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 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 uInt i; const uInt nOut = latticeOut.nelements(); AlwaysAssert(nOut > 0, AipsError); const IPosition shape(latticeOut[0]->shape()); const uInt outDim = shape.nelements(); for (i=1; ishape() == shape, AipsError); } // Make veracity check on input and first output lattice // and work out map to translate input and output axes. IPosition ioMap = prepare (latticeIn.shape(), 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; IPosition outPos(outDim, 0); IPosition outShape(outDim, 1); for (i=0; i= 0) { outShape(i) = len(ioMap(i)); } } // 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 it. Int nLine = outShape.product(); Int nResult = 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) { 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 the output buffer // The buffer contains nOut arrays (and is filled that way). uInt n = outShape.product(); Block block(n*nOut); Block blockMask(n*nOut); U* data = block.storage(); Bool* dataMask = blockMask.storage(); Vector result(nOut); Vector resultMask(nOut); for (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.multiProcess (result, resultMask, inIter.vectorCursor(), mask, pos); DebugAssert (result.nelements() == nOut, AipsError); U* datap = data+i; Bool* dataMaskp = dataMask+i; for (uInt j=0; jnstepsDone (inIter.nsteps()); } // Write the arrays (one in each output lattice). for (uInt k=0; k tmp (outShape, data + k*n, SHARE); latticeOut[k]->putSlice (tmp, outPos); if (latticeOut[k]->hasPixelMask()) { Lattice& maskOut = latticeOut[k]->pixelMask(); if (maskOut.isWritable()) { Array tmpMask (outShape, dataMask + k*n, SHARE); maskOut.putSlice (tmpMask, outPos); } } } } if (tellProgress != 0) tellProgress->done(); } template 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 template class Vector; template class Matrix; 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-2.4.1/lattices/LatticeMath/LatticeCleaner.h000066400000000000000000000277411321422335000223430ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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(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-2.4.1/lattices/LatticeMath/LatticeCleaner.tcc000066400000000000000000001111501321422335000226510ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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(Lattice & mask, const T& maskThreshold) { itsMaskThreshold = maskThreshold; IPosition maskShape = mask.shape(); IPosition dirtyShape = itsDirty->shape(); AlwaysAssert((mask.shape() == itsDirty->shape()), AipsError); // This is not needed after the first steps itsMask = new TempLattice(mask.shape(), 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-2.4.1/lattices/LatticeMath/LatticeConvolver.h000066400000000000000000000231371321422335000227420ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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: // The default constructor creates a LatticeConvolver that will convolve your // data with a point spread function (psf) that zero everywhere except at the // centre where it is one. Convolving with this psf will not change your // data. LatticeConvolver(); // 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-2.4.1/lattices/LatticeMath/LatticeConvolver.tcc000066400000000000000000000371011321422335000232600ustar00rootroot00000000000000// -*- 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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() :itsPsfShape(IPosition(1,1)), itsModelShape(itsPsfShape), itsType(ConvEnums::CIRCULAR), itsFFTShape(IPosition(1,1)), itsXfr(0), itsPsf(0), itsCachedPsf(False) { itsXfr->set(typename NumericTraits::ConjugateType(1)); doFast_p=False; } 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-2.4.1/lattices/LatticeMath/LatticeFFT.cc000066400000000000000000000366161321422335000215500ustar00rootroot00000000000000// -*- 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN 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(Complex)*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 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); } } 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(DComplex)*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 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); } } 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 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); } } } } 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 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); } } } } 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 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); } } } } void LatticeFFT::cfft(Lattice& cLattice, const Bool toFrequency) { const Vector whichAxes(cLattice.ndim(), True); LatticeFFT::cfft(cLattice, whichAxes, toFrequency); } void LatticeFFT::cfft(Lattice& cLattice, const Bool toFrequency) { const Vector whichAxes(cLattice.ndim(), True); LatticeFFT::cfft(cLattice, whichAxes, toFrequency); } void LatticeFFT::rcfft(Lattice& out, const 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; DebugAssert(outShape.isEqual(out.shape()), AipsError); // if (outShape.product() == 1) { // const IPosition origin(ndim, 0); // Float val = in.getAt(origin); // out.set(Complex(val, 0)); // return; // } const IPosition tileShape = out.niceCursorShape(); TempLattice inlocal(TiledShape(in.shape(), tileShape)); inlocal.put(in.get()); FFTServer ffts; { for (uInt dim = 0; dim < ndim; dim++) { if (whichAxes(dim) == True) { if (dim == firstAxis) { if (inShape(dim) != 1) { // Do real->complex Transforms LatticeIterator 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-------------------------------------- // void LatticeFFT::myrcfft(Lattice& out, const Lattice& 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); // outShape(0) = (inShape(0)+2)/2; // outShape(1) = 2*(inShape(1)/2+1); // if (outShape.product() == 1) { // const IPosition origin(ndim, 0); // Float val = in.getAt(origin); // out.set(Complex(val, 0)); // return; // } const IPosition tileShape = out.niceCursorShape(); FFTServer 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 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 &)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); } } } } } } } } // //------------------------------------------------------------------------- // void LatticeFFT::rcfft(Lattice& out, const Lattice& in, const Bool doShift, Bool doFast){ const Vector whichAxes(in.ndim(), True); LatticeFFT::rcfft(out, in, whichAxes, doShift, doFast); } void LatticeFFT::myrcfft(Lattice& out, const Lattice& in, const Bool doShift){ const Vector whichAxes(in.ndim(), True); LatticeFFT::myrcfft(out, in, whichAxes, doShift); } void LatticeFFT::crfft(Lattice& 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 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 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(real(in))); } } } } } void LatticeFFT::crfft(Lattice& out, Lattice& in, const Bool doShift, Bool doFast){ const Vector whichAxes(in.ndim(), True); LatticeFFT::crfft(out, in, whichAxes, doShift, doFast); } void LatticeFFT::crfft(Lattice& 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 casacore-2.4.1/lattices/LatticeMath/LatticeFFT.h000066400000000000000000000121721321422335000214010ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #ifndef LATTICES_LATTICEFFT_H #define LATTICES_LATTICEFFT_H #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template class Vector; 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. static void cfft2d(Lattice & cLattice, const Bool toFrequency=True); 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. static void cfft(Lattice & cLattice, const Vector & whichAxes, const Bool toFrequency=True); static void cfft(Lattice & cLattice, const Vector & whichAxes, const Bool toFrequency=True); // Non-folded version static void cfft0(Lattice & cLattice, const Vector & whichAxes, const Bool toFrequency=True); // N-D in-place complex->complex FFT. Transform over all axes. static void cfft(Lattice & cLattice, const Bool toFrequency=True); 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,...] static void rcfft(Lattice & out, const Lattice & in, const Vector & whichAxes, const Bool doShift=True, Bool doFast=False); static void myrcfft(Lattice & out, const Lattice & 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,...] static void rcfft(Lattice & out, const Lattice & in, const Bool doShift=True, Bool doFast=False); static void myrcfft(Lattice & out, const Lattice & 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. // static void crfft(Lattice & out, Lattice & in, const Vector & whichAxes, const Bool doShift=True, Bool doFast=False); static void crfft(Lattice & out, Lattice & in, const Bool doShift=True, Bool doFast=False); static void crfft(Lattice & out, const Lattice & in, const Bool doShift=True, Bool doFast=False); // }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/lattices/LatticeMath/LatticeFit.cc000066400000000000000000000223201321422335000216360ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { ok = False; } } else { try { sol.assign(fitter.fit(x, data, &inMask)); } catch (AipsError 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-2.4.1/lattices/LatticeMath/LatticeFit.h000066400000000000000000000112011321422335000214740ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/LatticeMath/LatticeFractile.h000066400000000000000000000161171321422335000225160ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef LATTICES_LATTICEFRACTILE_H #define LATTICES_LATTICEFRACTILE_H //# Includes #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations template class Lattice; template class MaskedLattice; template class Vector; 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-2.4.1/lattices/LatticeMath/LatticeFractile.tcc000066400000000000000000000762441321422335000230470ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/LatticeMath/LatticeHistProgress.cc000066400000000000000000000045611321422335000235570ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ // #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-2.4.1/lattices/LatticeMath/LatticeHistProgress.h000066400000000000000000000060261321422335000234170ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/LatticeMath/LatticeHistSpecialize.cc000066400000000000000000000303521321422335000240400ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ // #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 Float* pInData, const Bool* pInMask, Block* pHist, const Vector& clip, Float binWidth, uInt offset, uInt nrval, uInt nBins, uInt dataIncr, uInt maskIncr ) { Float 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; Float& 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; Float& hist = (*pHist)[index]; hist += 1.0; } pInData += dataIncr; pInMask += maskIncr; } } } 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 * C::_1_sqrt2 * C::_1_sqrtpi / dSigma; const Float gWidth = sqrt(8.0*C::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, Float& yMax, uInt nBins, Float scale) { counts(0) = scale * counts(0); for (uInt i=1; 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, Float& yMax, uInt nBins) { yMax = 0.0; for (uInt i=0; i 0.0) counts(i) = log10(counts(i)); yMax = max(yMax, counts(i)); } } void LatticeHistSpecialize::makeLogarithmic (Vector& 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 namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations template class Vector; template class Block; 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 static void makeCumulative (Vector& counts, Float& yMax, uInt nBins, Float scale); static void makeCumulative (Vector& counts, Complex& yMax, uInt nBins, Float scale); // Make histogram logarithmic static void makeLogarithmic (Vector& counts, Float& 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. static void process( const Float* pInData, const Bool* pInMask, Block* pHist, const Vector& clip, Float 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 #endif casacore-2.4.1/lattices/LatticeMath/LatticeHistograms.h000066400000000000000000000464551321422335000231150ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef LATTICES_LATTICEHISTOGRAMS_H #define LATTICES_LATTICEHISTOGRAMS_H //# Includes #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations template class MaskedLattice; template class TempLattice; template class Vector; 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-2.4.1/lattices/LatticeMath/LatticeHistograms.tcc000066400000000000000000001071721321422335000234310ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: LatticeHistograms.tcc 21563 2015-02-16 07:05:15Z gervandiepen $ #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; } T* dummy = 0; DataType latticeType = whatType(dummy); if (latticeType !=TpFloat && latticeType != TpComplex) { 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 T* dummy = 0; DataType type = whatType(dummy); 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 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); // T* dummy = 0; DataType type = whatType(dummy); 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 namespace casacore { //# NAMESPACE CASACORE - BEGIN template class Array; template class Lattice; template class MaskedLattice; template class MaskedArray; 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-2.4.1/lattices/LatticeMath/LatticeMathUtil.tcc000066400000000000000000000125201321422335000230300ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: LatticeUtilities.tcc 21531 2014-12-24 11:46:02Z gervandiepen $ #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-2.4.1/lattices/LatticeMath/LatticeProgress.cc000066400000000000000000000032421321422335000227220ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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-2.4.1/lattices/LatticeMath/LatticeProgress.h000066400000000000000000000074471321422335000225770ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef LATTICES_LATTICEPROGRESS_H #define LATTICES_LATTICEPROGRESS_H //# Includes #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations template class Vector; 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-2.4.1/lattices/LatticeMath/LatticeSlice1D.h000066400000000000000000000125141321422335000222060ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/LatticeMath/LatticeSlice1D.tcc000066400000000000000000000211301321422335000225220ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/LatticeMath/LatticeStatistics.h000066400000000000000000000645771321422335000231340ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 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; // DEPRECATED. WILL BE REMOVED, USE scimath/Mathematics/StatisticsAlgorithmFactory // instead struct AlgConf { StatisticsData::ALGORITHM algorithm; // hinges-fences f factor Double hf; // fit to have center type FitToHalfStatisticsData::CENTER ct; // fit to half data portion to use FitToHalfStatisticsData::USE_DATA ud; // fit to half center value AccumType cv; // Chauvenet zscore Double zs; // Chauvenet max iterations Int mi; }; // 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;}; // 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; // void configureClassical(); void configureClassical(Double aOld, Double bOld, Double aNew, Double bNew); // // 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 ); // get number of iterations associated with Chauvenet criterion algorithm std::map getChauvenetNiter() const { return _chauvIters; } 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); /* // Gets labels for higher order axes and x axis. // dPos is the location of the start of the cursor in the // storage image for this row. virtual void getLabels(String& higherOrderLabel, String& xAxisLabel, const IPosition& dPos) const; */ // 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 ); private: const MaskedLattice* pInLattice_p; SHARED_PTR > _inLatPtrMgr; CountedPtr > 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; 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); // 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, CountedPtr progressMeter); 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); } }; //# Declare extern templates for often used types. #ifdef AIPS_CXX11 extern template class LatticeStatistics; #endif } //# NAMESPACE CASA - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-2.4.1/lattices/LatticeMath/LatticeStatistics.tcc000066400000000000000000002263061321422335000234440ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: LatticeStatistics.tcc 21586 2015-03-25 13:46:25Z gervandiepen $ #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 #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() { 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() { 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) // // 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 if (! pStoreLattice_p.null()) { // delete pStoreLattice_p; pStoreLattice_p = 0; } 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; } 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 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.nelements() != range_p.nelements()) || (!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; } T* dummy = 0; DataType latticeType = whatType(dummy); if (latticeType !=TpFloat && latticeType!=TpComplex) { 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 Bool LatticeStatistics::getStatistic( Array& stats, LatticeStatsBase::StatisticsTypes type, Bool dropDeg ) { 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) { // 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) { return calculateStatistic (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(SIGMA) = sqrt(stats(VARIANCE)); 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) { 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 variance; retrieveStorageStatistic (variance, VARIANCE, dropDeg); ReadOnlyVectorIterator varianceIt(variance); while (!nPtsIt.pastEnd()) { for (uInt i=0; i sumSqIt(sumSq); AccumType npts = 0; while (!nPtsIt.pastEnd()) { for (uInt i=0; i void LatticeStatistics::configureClassical() { if (_saf.algorithm() != StatisticsData::CLASSICAL) { _saf.configureClassical(); needStorageLattice_p = True; } _setDefaultCoeffs(); } template void LatticeStatistics::configureClassical( Double aOld, Double bOld, Double aNew, Double bNew ) { if (_saf.algorithm() != StatisticsData::CLASSICAL) { _saf.configureClassical(); needStorageLattice_p = True; } _aOld = aOld; _bOld = bOld; _aNew = aNew; _bNew = bNew; } template void LatticeStatistics::configureHingesFences(Double f) { if ( _saf.algorithm() != StatisticsData::HINGESFENCES || ! near(f, _saf.hingesFencesFactor()) ) { _saf.configureHingesFences(f); needStorageLattice_p = True; } } template void LatticeStatistics::configureFitToHalf( FitToHalfStatisticsData::CENTER centerType, FitToHalfStatisticsData::USE_DATA useData, AccumType centerValue ) { Bool reconfig = _saf.algorithm() != StatisticsData::FITTOHALF; if (! reconfig) { typename StatisticsAlgorithmFactory::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; } } template void LatticeStatistics::configureChauvenet( Double zscore, Int maxIterations ) { Bool reconfig = _saf.algorithm() != StatisticsData::CHAUVENETCRITERION; if (! reconfig) { typename StatisticsAlgorithmFactory::ChauvenetData data = _saf.chauvenetData(); reconfig = ! near(zscore, data.zScore) || maxIterations != data.maxIter; } if (reconfig) { _saf.configureChauvenet(zscore, maxIterations); needStorageLattice_p = True; } } 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 = new TempLattice( TiledShape(storeLatticeShape, tileShape), useMemory ); // Set up min/max location variables CountedPtr pProgressMeter = showProgress_p ? new LattStatsProgress() : NULL; Double timeOld = 0; Double timeNew = 0; uInt nsets = pStoreLattice_p->size()/storeLatticeShape.getLast(1)[0]; Bool tryOldMethod = _saf.algorithm() == StatisticsData::CLASSICAL; if (tryOldMethod) { 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.nelements(); if (tryOldMethod) { // 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.null() ? NULL : &*pProgressMeter ); 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. } } if (! ranOldMethod) { _doStatsLoop(nsets, pProgressMeter); } pProgressMeter = NULL; if (doRobust_p) { // Do robust statistics separately as required. generateRobust(); } needStorageLattice_p = False; doneSomeGoodPoints_p = False; return True; } template void LatticeStatistics::_doStatsLoop( uInt nsets, CountedPtr progressMeter ) { // use high performance method for low iteration count maxPos_p.resize(0); minPos_p.resize(0); const uInt nCursorAxes = cursorAxes_p.nelements(); const IPosition latticeShape(pInLattice_p->shape()); IPosition cursorShape(pInLattice_p->ndim(),1); for (uInt i=0; i > sa = _saf.createStatsAlgorithm(); Bool isChauv = _saf.algorithm() == StatisticsData::CHAUVENETCRITERION; LatticeStatsDataProvider lattDP; MaskedLatticeStatsDataProvider maskedLattDP; LatticeStatsDataProviderBase *dataProvider; _configureDataProviders(lattDP, maskedLattDP); Bool nsetsIsLarge = nsets > 50; if (! progressMeter.null()) { if (nsetsIsLarge) { progressMeter->init(nsets); } else { lattDP.setProgressMeter(progressMeter); if (pInLattice_p->isMasked()) { maskedLattDP.setProgressMeter(progressMeter); } } } _chauvIters.clear(); IPosition curPos, posMax, posMean, posMin, posNpts, posSum, posSumsq, posVariance; Slicer slicer(stepper.position(), stepper.endPosition(), Slicer::endIsLast); SubLattice subLat(*pInLattice_p, slicer); StatsData stats; for (stepper.reset(); ! stepper.atEnd(); stepper++) { curPos = stepper.position(); posMax = locInStorageLattice(curPos, LatticeStatsBase::MAX); posMean = locInStorageLattice(curPos, LatticeStatsBase::MEAN); posMin = locInStorageLattice(curPos, LatticeStatsBase::MIN); posNpts = locInStorageLattice(curPos, LatticeStatsBase::NPTS); posSum = locInStorageLattice(curPos, LatticeStatsBase::SUM); posSumsq = locInStorageLattice(curPos, LatticeStatsBase::SUMSQ); posVariance = locInStorageLattice(curPos, LatticeStatsBase::VARIANCE); slicer.setStart(curPos); slicer.setEnd(stepper.endPosition()); subLat.setRegion(slicer); if(subLat.isMasked()) { maskedLattDP.setLattice(subLat); dataProvider = &maskedLattDP; } else { lattDP.setLattice(subLat); dataProvider = &lattDP; } if ( stepper.atStart() && ! progressMeter.null() && ! nsetsIsLarge ) { progressMeter->init(nsets*dataProvider->estimatedSteps()); } sa->setDataProvider(dataProvider); stats = sa->getStatistics(); if (isChauv) { ChauvenetCriterionStatistics *ch = dynamic_cast *>( &*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(); } // FIXME it is likely that these putAt() calls are the source of the performance // degradation in the case where the old method is faster. Eg consider a lattice // with shape 1000 x 1000 x 3 doing stats with a cursor axis of 2 so that 1000 x 1000 // sets of stats are computed. That's 1e6 putAt calls for *each* statistic. A relatively // small array that holds many statistics sets should probably be used as temporary storage, // and when the array is full it should be inserted into the storage lattice using // putSlice(). This is how the old method manages puts, and probably is why it is // so much faster in these types of cases. pStoreLattice_p->putAt(stats.mean, posMean); pStoreLattice_p->putAt(stats.npts, posNpts); pStoreLattice_p->putAt(stats.sum, posSum); pStoreLattice_p->putAt(stats.sumsq, posSumsq); pStoreLattice_p->putAt(stats.variance, posVariance); if (fixedMinMax_p && ! noInclude_p) { currentMax = range_p[1]; } else { currentMax = stats.max.null() ? 0 : *stats.max; } pStoreLattice_p->putAt(currentMax, posMax); if (fixedMinMax_p && ! noInclude_p) { currentMin = range_p[0]; } else { currentMin = stats.min.null() ? 0 : *stats.min; } pStoreLattice_p->putAt(currentMin, posMin); if (isReal) { // 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 (! fixedMinMax_p || noInclude_p) { if (stepper.atStart()) { IPosition myMaxPos, myMinPos; dataProvider->minMaxPos(myMinPos, myMaxPos); if (myMinPos.size() > 0) { minPos_p = subLat.positionInParent(myMinPos); } if (myMaxPos.size() > 0) { maxPos_p = subLat.positionInParent(myMaxPos); } overallMin = currentMin; overallMax = currentMax; } else if ( currentMax > overallMax || currentMin < overallMin ) { IPosition myMaxPos, myMinPos; dataProvider->minMaxPos(myMinPos, myMaxPos); if (currentMin < overallMin) { if (myMinPos.size() > 0) { minPos_p = subLat.positionInParent(myMinPos); } overallMin = currentMin; } if (currentMax > overallMax) { if (myMaxPos.size() > 0) { maxPos_p = subLat.positionInParent(myMaxPos); } overallMax = currentMax; } } } } if(! progressMeter.null() && nsetsIsLarge) { (*progressMeter)++; } } 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 (see above FIXME comment // in the current method that explains that). uInt ndim = pStoreLattice_p->ndim(); IPosition 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 void LatticeStatistics::generateRobust () { Bool showMsg = haveLogger_p && displayAxes_p.nelements()==0; if (showMsg) { os_p << LogIO::NORMAL << "Computing quantiles..." << LogIO::POST; } const uInt nCursorAxes = cursorAxes_p.nelements(); const IPosition latticeShape(pInLattice_p->shape()); IPosition cursorShape(pInLattice_p->ndim(),1); for (uInt i=0; i fractions; CountedPtr > sa; LatticeStatsDataProvider lattDP; MaskedLatticeStatsDataProvider maskedLattDP; IPosition curPos, pos, pos2, pos3, posQ1, posQ3, posNpts, posMax, posMin; Slicer slicer; SubLattice subLat; std::map quantiles; CountedPtr knownNpts; CountedPtr knownMax, knownMin; static const uInt maxArraySizeBytes = 1e8; fractions.insert(0.25); fractions.insert(0.75); sa = _saf.createStatsAlgorithm(); _configureDataProviders(lattDP, maskedLattDP); slicer = Slicer(stepper.position(), stepper.endPosition(), Slicer::endIsLast); subLat = SubLattice(*pInLattice_p, slicer); 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 = new uInt64((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); quantiles.clear(); 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); } // computing the median and the quartiles simultaneously minimizes // the number of necessary data scans, as opposed to first calling // getMedian() and getQuartiles() separately knownMin = new AccumType(pStoreLattice_p->getAt(posMin)); knownMax = new AccumType(pStoreLattice_p->getAt(posMax)); Int64 nBins = 10000; if (knownNpts) { // try to prevent multiple passes for // large images if (*knownNpts > 1e10) { nBins = 1e7; } else if (*knownNpts > 1e9) { nBins = 1e6; } else if (*knownNpts > 1e8) { nBins = 1e5; } } pStoreLattice_p->putAt( sa->getMedianAndQuantiles( quantiles, fractions, knownNpts, knownMin, knownMax, maxArraySizeBytes, False, nBins ), pos ); pStoreLattice_p->putAt( sa->getMedianAbsDevMed( knownNpts, knownMin, knownMax, maxArraySizeBytes, False, nBins ), pos2 ); pStoreLattice_p->putAt(quantiles[0.75] - quantiles[0.25], pos3); pStoreLattice_p->putAt(quantiles[0.25], posQ1); pStoreLattice_p->putAt(quantiles[0.75], posQ3); } } 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; T* dummy = 0; DataType type = whatType(dummy); 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; T* dummy = 0; DataType type = whatType(dummy); 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; T* dummy = 0; DataType type = whatType(dummy); if (type==TpComplex) { oDWidth = 2*oDWidth + 3; } sprintf( buffer, "%d", nPts ); stats.push_back(stat_element("Npts",buffer)); sprintf( buffer, "%e", sum ); stats.push_back(stat_element("Sum",buffer)); if ( _canDoFlux()) { Bool unused; sprintf( buffer, "%e", _flux(unused, sum, area ).getValue()); stats.push_back(stat_element("FluxDensity",buffer)); } sprintf( buffer, "%e", mean ); stats.push_back(stat_element("Mean",buffer)); if (doRobust_p) { sprintf( buffer, "%e", median ); stats.push_back(stat_element("Median",buffer)); } sprintf( buffer, "%e", rms ); stats.push_back(stat_element("Rms",buffer)); sprintf( buffer, "%e", sigma ); stats.push_back(stat_element("Std dev",buffer)); sprintf( buffer, "%e", dMin ); stats.push_back(stat_element("Minimum",buffer)); sprintf( 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 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){ sprintf( buffer, "%e", ord.column(SUM)(j) ); stats.push_back(stat_element("Sum",buffer)); if (_canDoFlux()) { sprintf( buffer, "%e", ord.column(FLUX)(j) ); stats.push_back(stat_element("FluxDensity",buffer)); } sprintf( buffer, "%e", ord.column(MEAN)(j) ); stats.push_back(stat_element("Mean",buffer)); if (doRobust_p){ sprintf( buffer, "%e", ord.column(MEDIAN)(j) ); stats.push_back(stat_element("Median",buffer)); } sprintf( buffer, "%e", ord.column(RMS)(j) ); stats.push_back(stat_element("Rms",buffer)); sprintf( buffer, "%e", ord.column(SIGMA)(j) ); stats.push_back(stat_element("Std dev",buffer)); sprintf( buffer, "%e", ord.column(MIN)(j) ); stats.push_back(stat_element("Minimum",buffer)); sprintf( 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; T* dummy = 0; DataType type = whatType(dummy); 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 void LatticeStatistics::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 // { ostringstream oss0; oss0 << "Axis " << displayAxes_p(0)+1 << " (pixels)"; xLabel = oss0.str(); // const uInt n = displayAxes_p.nelements(); hLabel =String(""); if (n > 1) { ostringstream oss; for (uInt j=1; j 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); AccumType sigma = sqrt(var); 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; T* dummy = 0; DataType type = whatType(dummy); 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-2.4.1/lattices/LatticeMath/LatticeStatsBase.cc000066400000000000000000000136701321422335000230150ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN Vector LatticeStatsBase::toStatisticTypes (const String& statsU, const 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 #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) NACCUM = VARIANCE+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 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); }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/lattices/LatticeMath/LatticeStatsDataProvider.h000066400000000000000000000120511321422335000243610ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: Array.h 21545 2015-01-22 19:36:35Z gervandiepen $ #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: CountedPtr > _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-2.4.1/lattices/LatticeMath/LatticeStatsDataProvider.tcc000066400000000000000000000124171321422335000247110ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: Array.h 21545 2015-01-22 19:36:35Z gervandiepen $ #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.null()) { _atEnd = True; } else { ++(*_iter); } this->_updateProgress(); } template uInt LatticeStatsDataProvider::estimatedSteps() const { if (_iter.null()) { 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.null()) { return _atEnd; } return _iter->atEnd(); } template void LatticeStatsDataProvider::finalize() { _freeStorage(); LatticeStatsDataProviderBase::finalize(); } template uInt64 LatticeStatsDataProvider::getCount() { if (_iter.null()) { return _currentSlice.size(); } return _iter->cursor().size(); } template const T* LatticeStatsDataProvider::getData() { if (! _iter.null()) { _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.null()) { _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 = new RO_LatticeIterator(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.null()) { p += _iter->position(); } this->_updateMaxPos(p); } template void LatticeStatsDataProvider::updateMinPos( const std::pair& minpos ) { IPosition p = toIPositionInArray(minpos.second, _currentSlice.shape()); if (! _iter.null()) { p += _iter->position(); } this->_updateMinPos(p); } template void LatticeStatsDataProvider::_freeStorage() { _currentSlice.freeStorage (_currentPtr, _delData); _delData = False; } } #endif casacore-2.4.1/lattices/LatticeMath/LatticeStatsDataProviderBase.h000066400000000000000000000075521321422335000251660ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: Array.h 21545 2015-01-22 19:36:35Z gervandiepen $ #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: //typedef typename NumericTraits::PrecisionType AccumType; 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(CountedPtr pm); // set the data ranges void setRanges( const std::vector::PrecisionType, typename NumericTraits::PrecisionType> >& ranges, Bool isInclude ); protected: LatticeStatsDataProviderBase(); //virtual uInt _nsteps() const = 0; 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; CountedPtr _progressMeter; IPosition _minPos, _maxPos; }; } #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-2.4.1/lattices/LatticeMath/LatticeStatsDataProviderBase.tcc000066400000000000000000000070121321422335000254770ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: Array.h 21545 2015-01-22 19:36:35Z gervandiepen $ #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); /* if (! _progressMeter.null()) { cout << "reset progress" << endl; _progressMeter->init(_progressMeter->expectedNsteps()); } */ } template void LatticeStatsDataProviderBase::setProgressMeter( CountedPtr 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.null()) { (*_progressMeter)++; } } } #endif casacore-2.4.1/lattices/LatticeMath/LatticeTwoPtCorr.h000066400000000000000000000105721321422335000226670ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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-2.4.1/lattices/LatticeMath/LatticeTwoPtCorr.tcc000066400000000000000000000242101321422335000232030ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef LATTICES_LATTICETWOPTCORR_TCC #define LATTICES_LATTICETWOPTCORR_TCC #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-2.4.1/lattices/LatticeMath/LineCollapser.h000066400000000000000000000123631321422335000222120ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef LATTICES_LINECOLLAPSER_H #define LATTICES_LINECOLLAPSER_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations template class Vector; 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-2.4.1/lattices/LatticeMath/LineCollapser.tcc000066400000000000000000000031501321422335000225260ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/LatticeMath/MaskedLatticeStatsDataProvider.h000066400000000000000000000122501321422335000255070ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: Array.h 21545 2015-01-22 19:36:35Z gervandiepen $ #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: CountedPtr > _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-2.4.1/lattices/LatticeMath/MaskedLatticeStatsDataProvider.tcc000066400000000000000000000142571321422335000260420ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: Array.h 21545 2015-01-22 19:36:35Z gervandiepen $ #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.null()) { _atEnd = True; } else { ++(*_iter); } this->_updateProgress(); } template uInt MaskedLatticeStatsDataProvider::estimatedSteps() const { if (_iter.null()) { 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.null()) { return _atEnd; } else { return _iter->atEnd(); } } template void MaskedLatticeStatsDataProvider::finalize() { LatticeStatsDataProviderBase::finalize(); _freeStorage(); } template uInt64 MaskedLatticeStatsDataProvider::getCount() { if (_iter.null()) { return _currentSlice.size(); } else { return _iter->cursor().size(); } } template const T* MaskedLatticeStatsDataProvider::getData() { if (! _iter.null()) { _currentSlice.assign(_iter->cursor()); } _currentPtr = _currentSlice.getStorage(_delData); return _currentPtr; } template const Bool* MaskedLatticeStatsDataProvider::getMask() { if (! _iter.null()) { _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.null()) { _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 = new RO_MaskedLatticeIterator(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.null()) { p += _iter->position(); } this->_updateMaxPos(p); } template void MaskedLatticeStatsDataProvider::updateMinPos( const std::pair& minpos ) { IPosition p = toIPositionInArray(minpos.second, _currentSlice.shape()); if (! _iter.null()) { p += _iter->position(); } this->_updateMinPos(p); } template void MaskedLatticeStatsDataProvider::_freeStorage() { _currentSlice.freeStorage (_currentPtr, _delData); _delData = False; _currentMaskSlice.freeStorage(_currentMaskPtr, _delMask); _delMask = False; } /* template uInt MaskedLatticeStatsDataProvider::_nsteps() const { const IPosition trc = _iter.latticeShape() - 1; uInt ndim = trc.size(); const IPosition blc(ndim, 0); const IPosition tileShape = _iter.lattice().niceCursorShape(); uInt nsteps = 1; for (uInt j=0; j //# //# $Id: HostInfoDarwin.h 21521 2014-12-10 08:06:42Z gervandiepen $ #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-2.4.1/lattices/LatticeMath/MultiTermLatticeCleaner.tcc000066400000000000000000001165161321422335000245270ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: MultiTermLatticeCleaner.cc 19909 2008-04-23 02:08:02Z UrvashiRau $ #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-2.4.1/lattices/LatticeMath/StatsTiledCollapser.h000066400000000000000000000153541321422335000234060ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: LatticeStatistics.h 20739 2009-09-29 01:15:15Z Malte.Marquarding $ #ifndef LATTICES_STATSTILEDCOLLAPSER_H #define LATTICES_STATSTILEDCOLLAPSER_H //# Includes #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 CountedPtr > _npts; CountedPtr > _sum, _sumSq, _mean, _variance, _nvariance; CountedPtr > _min, _max; CountedPtr > _initMinMax; uInt64 _n1, _n3; void _convertNPts( Double*& nptsPtr, CountedPtr > npts, CountedPtr > nptsComplex ) const; void _convertNPts( DComplex*& nptsPtr, CountedPtr > npts, CountedPtr > nptsComplex ) const; }; } #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif #endif casacore-2.4.1/lattices/LatticeMath/StatsTiledCollapser.tcc000066400000000000000000000231511321422335000237220ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: LatticeStatistics.tcc 20652 2009-07-06 05:04:32Z Malte.Marquarding $ #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(&*(CountedPtr(new T(0)))))), _minpos(0), _maxpos(0) {} template void StatsTiledCollapser::init (uInt nOutPixelsPerCollapse) { AlwaysAssert (nOutPixelsPerCollapse == LatticeStatsBase::NACCUM, AipsError); } template void StatsTiledCollapser::initAccumulator (uInt64 n1, uInt64 n3) { _sum = new Block(n1*n3); _sumSq = new Block(n1*n3); _npts = new Block(n1*n3); _mean = new Block(n1*n3); _variance = new Block(n1*n3); _nvariance = new Block(n1*n3); _min = new Block(n1*n3); _max = new Block(n1*n3); _initMinMax = new Block(n1*n3); _sum->set(0); _sumSq->set(0); _npts->set(0); _mean->set(0); _variance->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& nvariance = (*_nvariance)[index]; // If these are != -1 after the accumulating, then // the min and max were updated Int64 minLoc = -1; Int64 maxLoc = -1; 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; // 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(); CountedPtr > nptsComplex; if (! isReal(whatType(resptr))) { nptsComplex = new Block(_n1*_n3); } U* nPtsPtr; _convertNPts(nPtsPtr, _npts, nptsComplex); U* meanPtr = _mean->storage(); U* variancePtr = _variance->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::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, CountedPtr > npts, CountedPtr > ) const { nptsPtr = npts->storage(); } template void StatsTiledCollapser::_convertNPts( DComplex*& nptsPtr, CountedPtr > npts, CountedPtr > 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-2.4.1/lattices/LatticeMath/TiledCollapser.h000066400000000000000000000144071321422335000223650ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef LATTICES_TILEDCOLLAPSER_H #define LATTICES_TILEDCOLLAPSER_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations template class Array; 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-2.4.1/lattices/LatticeMath/TiledCollapser.tcc000066400000000000000000000031571321422335000227070ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/LatticeMath/test/000077500000000000000000000000001321422335000202575ustar00rootroot00000000000000casacore-2.4.1/lattices/LatticeMath/test/CMakeLists.txt000066400000000000000000000007651321422335000230270ustar00rootroot00000000000000set (tests tFit2D tLatticeAddNoise tLatticeApply tLatticeApply2 tLatticeConvolver tLatticeFFT tLatticeFit tLatticeFractile tLatticeHistograms tLatticeMathUtil tLatticeSlice1D tLatticeStatistics tLatticeStatsDataProvider tLatticeTwoPtCorr tLattStatsSpecialize ) foreach (test ${tests}) add_executable (${test} ${test}.cc) target_link_libraries (${test} casa_lattices) add_test (${test} ${CMAKE_SOURCE_DIR}/cmake/cmake_assay ./${test}) add_dependencies(check ${test}) endforeach (test) casacore-2.4.1/lattices/LatticeMath/test/tFit2D.cc000066400000000000000000000271161321422335000216710ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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") * C::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 += C::pi / 180 * 20.0; if (pa > C::pi) pa -= C::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; } } 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 (AipsError x) { cout << "Failed with message " << x.getMesg() << 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 int main() { try { { Vector mySamples(10); Double nPts = 0; Double sum = 0; Double mean = 0; Double nvariance = 0; Double variance = 0; Double sumSq = 0; Float dataMin, dataMax; Int minPos, maxPos; Bool minMaxInit = True; Bool fixedMinMax = False; uInt pos = 0; Bool useIt = True; for ( Vector::iterator iter=mySamples.begin(); iter != mySamples.end(); iter++ ) { *iter = 2*nPts; Float datum = *iter; LattStatsSpecialize::accumulate ( nPts, sum, mean, nvariance, variance, sumSq, dataMin, dataMax, minPos, maxPos, minMaxInit, fixedMinMax, datum, pos, useIt ); pos++; } AlwaysAssert(nPts == 10, AipsError); AlwaysAssert(sum == 90, AipsError); AlwaysAssert(mean == 9, AipsError); AlwaysAssert(near(variance, 36.6666666666, 1e-11), AipsError); AlwaysAssert(sumSq == 1140, AipsError); AlwaysAssert(dataMin == 0, AipsError); AlwaysAssert(dataMax == 18, AipsError); AlwaysAssert(minPos == 0, AipsError); AlwaysAssert(maxPos == 9, AipsError); } { Vector mySamples(10); DComplex nPts = 0; DComplex sum = 0; DComplex mean = 0; DComplex nvariance = 0; DComplex variance = 0; DComplex sumSq = 0; Complex dataMin, dataMax; Int minPos, maxPos; Bool minMaxInit; Bool fixedMinMax = False; uInt pos = 0; Complex useIt(1, 1); Double count = 0; for ( Vector::iterator iter=mySamples.begin(); iter != mySamples.end(); iter++ ) { *iter = Complex(2*count, count); Complex datum = *iter; LattStatsSpecialize::accumulate ( nPts, sum, mean, nvariance, variance, sumSq, dataMin, dataMax, minPos, maxPos, minMaxInit, fixedMinMax, datum, pos, useIt ); pos++; count++; } AlwaysAssert(nPts == DComplex(10, 10), AipsError); AlwaysAssert(sum == DComplex(90, 45), AipsError); AlwaysAssert(mean == DComplex(9, 4.5), AipsError); AlwaysAssert(near(variance.real(), 36.6666666666, 1e-11), AipsError); AlwaysAssert(near(variance.imag(), 9.166666666, 1e-10), AipsError); AlwaysAssert(sumSq == DComplex(1140, 285), AipsError); AlwaysAssert(dataMin == Complex(0, 0), AipsError); AlwaysAssert(dataMax == Complex(18, 9), AipsError); /* AlwaysAssert(sumSq == 1140, AipsError); AlwaysAssert(dataMin == 0, AipsError); AlwaysAssert(dataMax == 18, AipsError); AlwaysAssert(minPos == 0, AipsError); AlwaysAssert(maxPos == 9, AipsError); */ } } catch (AipsError x) { cerr << "aipserror: error " << x.getMesg() << endl; return 1; } return 0; } casacore-2.4.1/lattices/LatticeMath/test/tLatticeAddNoise.cc000066400000000000000000000146601321422335000237550ustar00rootroot00000000000000//# tLatticeAddNoise.cc: Test program for class LatticeAddNoise //# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include #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 (AipsError x) { cerr << "Caught exception: " << x.getMesg() << 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-2.4.1/lattices/LatticeMath/test/tLatticeApply.cc000066400000000000000000000446551321422335000233630ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include #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 { { // test linear and circular convolution using the default psf. LatticeConvolver d; AlwaysAssert(d.shape() == IPosition(1,1), AipsError); AlwaysAssert(d.fftShape() == IPosition(1,1) , AipsError); AlwaysAssert(d.psfShape() == IPosition(1,1) , AipsError); AlwaysAssert(d.type() == ConvEnums::CIRCULAR , AipsError); TempLattice model(IPosition(1,9)); model.set(1.0f); model.putAt(2.0f, model.shape()/2); TempLattice result(model.shape()); d.circular(result, model); AlwaysAssert(allNearAbs(model.get(), result.get(), NumericTraits::epsilon), AipsError); AlwaysAssert(d.shape() == IPosition(1,9), AipsError); AlwaysAssert(d.fftShape() == IPosition(1,9) , AipsError); AlwaysAssert(d.psfShape() == IPosition(1,1) , AipsError); AlwaysAssert(d.type() == ConvEnums::CIRCULAR , AipsError); result.set(0.0f); d.linear(result, model); AlwaysAssert(allNear(model.get(), result.get(), NumericTraits::epsilon), AipsError); AlwaysAssert(d.shape() == IPosition(1,9), AipsError); AlwaysAssert(d.fftShape() == IPosition(1,9) , AipsError); AlwaysAssert(d.psfShape() == IPosition(1,1) , AipsError); AlwaysAssert(d.type() == ConvEnums::LINEAR , AipsError); } { 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 (AipsError x) { cout<< "FAIL"<< endl; cerr << x.getMesg() << endl; return 1; } cout<< "OK"<< endl; return 0; } // Local Variables: // compile-command: "gmake OPTLIB=1 tLatticeConvolver" // End: casacore-2.4.1/lattices/LatticeMath/test/tLatticeConvolver.run000066400000000000000000000000421321422335000244500ustar00rootroot00000000000000#! /bin/sh echo "UNTESTED" exit 3 casacore-2.4.1/lattices/LatticeMath/test/tLatticeFFT.cc000066400000000000000000000135551321422335000227100ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { cerr << x.getMesg() << endl; cout<< "FAIL"<< endl; } } // { // RO_LatticeIterator iter(rArr, rArr.shape()); // cout << iter.cursor() << endl; // } // Local Variables: // compile-command: "gmake OPTLIB=1 tLatticeFFT" // End: casacore-2.4.1/lattices/LatticeMath/test/tLatticeFit.cc000066400000000000000000000120271321422335000230040ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/LatticeMath/test/tLatticeFractile.cc000066400000000000000000000206541321422335000240200ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 AipsError& x) { cout << "Unexpected exception: " << x.getMesg() << endl; return 1; } return 0; } casacore-2.4.1/lattices/LatticeMath/test/tLatticeFractile.out000066400000000000000000000031021321422335000242270ustar00rootroot00000000000000>>> /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-2.4.1/lattices/LatticeMath/test/tLatticeHistograms.cc000066400000000000000000000247141321422335000244100ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: // #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.0); 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.0); 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 AipsError& x) { cerr << "aipserror: error " << x.getMesg() << 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-2.4.1/lattices/LatticeMath/test/tLatticeMathUtil.cc000066400000000000000000000122221321422335000240060ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: tLatticeUtilities.cc 21509 2014-11-21 12:25:09Z gervandiepen $ #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 AipsError& x) { cout<< "FAIL"<< endl; cerr << x.getMesg() << 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-2.4.1/lattices/LatticeMath/test/tLatticeSlice1D.cc000066400000000000000000000272541321422335000235160ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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 (AipsError x) { cerr << "Caught expected exception " << x.getMesg() << 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 (AipsError x) { cerr << "Caught expected exception " << x.getMesg() << 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-2.4.1/lattices/LatticeMath/test/tLatticeStatistics.cc000066400000000000000000001313131321422335000244140ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: tLatticeStatistics.cc 20650 2009-06-30 07:21:23Z gervandiepen $ // #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); //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 ); Array v; 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); } { 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); } } catch (const AipsError& x) { cerr << "aipserror: error " << x.getMesg() << 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); // Test 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 AipsError& x) { cerr << x.getMesg () << endl; cout << "FAIL" << endl; return 1; } cout << "OK" << endl; return 0; } casacore-2.4.1/lattices/LatticeMath/test/tLatticeTwoPtCorr.cc000066400000000000000000000110321321422335000241600ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { cerr << "Caught exception: " << x.getMesg() << endl; cout << "FAIL" << endl; return 1; } cout << "OK" << endl; return 0; } casacore-2.4.1/lattices/Lattices.h000066400000000000000000000466521321422335000170370ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/Lattices/000077500000000000000000000000001321422335000166515ustar00rootroot00000000000000casacore-2.4.1/lattices/Lattices/ArrayLattice.h000066400000000000000000000210741321422335000214120ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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-2.4.1/lattices/Lattices/ArrayLattice.tcc000066400000000000000000000121571321422335000217360ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 { 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-2.4.1/lattices/Lattices/CurvedLattice2D.h000066400000000000000000000164721321422335000217600ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/Lattices/CurvedLattice2D.tcc000066400000000000000000000165721321422335000223030ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/Lattices/ExtendLattice.h000066400000000000000000000163731321422335000215710ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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-2.4.1/lattices/Lattices/ExtendLattice.tcc000066400000000000000000000222151321422335000221030ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/Lattices/HDF5LattIter.h000066400000000000000000000113471321422335000211670ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/Lattices/HDF5LattIter.tcc000066400000000000000000000060321321422335000215040ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/Lattices/HDF5Lattice.h000066400000000000000000000250241321422335000210210ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 CountedPtr& 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 CountedPtr& 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 CountedPtr& file() const { return itsFile; } // Return the current HDF5Group object. const CountedPtr& group() const { return itsGroup; } // 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; CountedPtr itsFile; CountedPtr itsGroup; CountedPtr itsDataSet; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-2.4.1/lattices/Lattices/HDF5Lattice.tcc000066400000000000000000000224631321422335000213470ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef LATTICES_HDF5LATTICE_TCC #define LATTICES_HDF5LATTICE_TCC #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 = new HDF5File (fileName, ByteIO::New); makeArray (shape, arrayName, groupName); DebugAssert (ok(), AipsError); } template HDF5Lattice::HDF5Lattice (const TiledShape& shape) { Path fileName = File::newUniqueName(String("./"), String("HDF5Lattice")); itsFile = new HDF5File (fileName.absoluteName(), ByteIO::Scratch); makeArray (shape, "array", String()); DebugAssert (ok(), AipsError); } template HDF5Lattice::HDF5Lattice (const TiledShape& shape, const CountedPtr& 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 = new HDF5File(fileName, ByteIO::Update); } else { itsFile = new HDF5File(fileName); } openArray (arrayName, groupName); DebugAssert (ok(), AipsError); } template HDF5Lattice::HDF5Lattice (const CountedPtr& 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) { 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; } 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 itsDataSet->tileShape(); } 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(), itsDataSet->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 = new HDF5Group(*itsFile, "/", true); } else { itsGroup = new HDF5Group(*itsFile, groupName, true); } // Open the data set. itsDataSet = new HDF5DataSet (*itsGroup, arrayName, (const T*)0); } 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 = new HDF5Group(*itsFile, "/", true); } else { // Create group if not existing yet. itsGroup = new HDF5Group(*itsFile, groupName); } // Create the data set. itsDataSet = new HDF5DataSet (*itsGroup, arrayName, shape.shape(), shape.tileShape(), (const T*)0); } template void HDF5Lattice::flush() { itsFile->flush(); } } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/lattices/Lattices/Lattice.h000066400000000000000000000455671321422335000204300ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef LATTICES_LATTICE_H #define LATTICES_LATTICE_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class IPosition; class LatticeNavigator; template class Array; 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. #ifdef AIPS_CXX11 extern template class Lattice; extern template class Lattice; #endif } //# 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-2.4.1/lattices/Lattices/Lattice.tcc000066400000000000000000000246751321422335000207470ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 ((T*)0); } // 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. Array* arr = new Array; Bool isARef = This->getSlice (*arr, section, removeDegenerateAxes); buffer = COWPtr > (arr, True, 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); } } 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-2.4.1/lattices/Lattices/LatticeBase.cc000066400000000000000000000066651321422335000213550ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/Lattices/LatticeBase.h000066400000000000000000000241641321422335000212110ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/Lattices/LatticeCache.h000066400000000000000000000112411321422335000213320ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef LATTICES_LATTICECACHE_H #define LATTICES_LATTICECACHE_H //# Includes #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-2.4.1/lattices/Lattices/LatticeCache.tcc000066400000000000000000000241141321422335000216570ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/Lattices/LatticeConcat.h000066400000000000000000000233211321422335000215400ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/Lattices/LatticeConcat.tcc000066400000000000000000000546511321422335000220740ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/Lattices/LatticeIndexer.cc000066400000000000000000000345331321422335000220740ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ // #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-2.4.1/lattices/Lattices/LatticeIndexer.h000066400000000000000000000244031321422335000217310ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/Lattices/LatticeIterInterface.h000066400000000000000000000272151321422335000230630ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef LATTICES_LATTICEITERINTERFACE_H #define LATTICES_LATTICEITERINTERFACE_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations template class Vector; template class Matrix; template class Cube; 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. #ifdef AIPS_CXX11 extern template class LatticeIterInterface; #endif } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-2.4.1/lattices/Lattices/LatticeIterInterface.tcc000066400000000000000000000304311321422335000233770ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/Lattices/LatticeIterator.h000066400000000000000000000545451321422335000221360ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef LATTICES_LATTICEITERATOR_H #define LATTICES_LATTICEITERATOR_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class IPosition; class LatticeNavigator; template class Array; template class Cube; template class Matrix; template class Vector; // // 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.null(); } // 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 CountedPtr > 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-2.4.1/lattices/Lattices/LatticeIterator.tcc000066400000000000000000000177231321422335000224550ustar00rootroot00000000000000//# LatticeIter.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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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() { // CountedPtr 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 = 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 = 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-2.4.1/lattices/Lattices/LatticeLocker.cc000066400000000000000000000042251321422335000217100ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/Lattices/LatticeLocker.h000066400000000000000000000135771321422335000215640ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/Lattices/LatticeNavigator.cc000066400000000000000000000061451321422335000224260ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/Lattices/LatticeNavigator.h000066400000000000000000000374601321422335000222740ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/Lattices/LatticeStepper.cc000066400000000000000000000404741321422335000221210ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/Lattices/LatticeStepper.h000066400000000000000000000533201321422335000217550ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/Lattices/LatticeUtilities.h000066400000000000000000000077001321422335000223070ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef LATTICES_LATTICEUTILITIES_H #define LATTICES_LATTICEUTILITIES_H #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template class Array; template class Lattice; template class MaskedLattice; template class MaskedArray; 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-2.4.1/lattices/Lattices/LatticeUtilities.tcc000066400000000000000000000146231321422335000226330ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/Lattices/Lattices_1.fig000066400000000000000000000071151321422335000213340ustar00rootroot00000000000000#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-2.4.1/lattices/Lattices/Lattices_1.ps000066400000000000000000000147321321422335000212140ustar00rootroot00000000000000%!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-2.4.1/lattices/Lattices/Lattices_2.fig000066400000000000000000000147631321422335000213440ustar00rootroot00000000000000#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-2.4.1/lattices/Lattices/Lattices_2.ps000066400000000000000000000252521321422335000212140ustar00rootroot00000000000000%!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-2.4.1/lattices/Lattices/Lattices_3.fig000066400000000000000000000033511321422335000213340ustar00rootroot00000000000000#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-2.4.1/lattices/Lattices/Lattices_3.ps000066400000000000000000000107051321422335000212120ustar00rootroot00000000000000%!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-2.4.1/lattices/Lattices/Lattices_4.fig000066400000000000000000000124541321422335000213410ustar00rootroot00000000000000#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-2.4.1/lattices/Lattices/Lattices_4.ps000066400000000000000000000200571321422335000212140ustar00rootroot00000000000000%!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-2.4.1/lattices/Lattices/Lattices_tmpl.cc000066400000000000000000000036021321422335000217650ustar00rootroot00000000000000//# 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: aips2-request@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. #ifdef AIPS_CXX11 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; } #endif casacore-2.4.1/lattices/Lattices/MaskedLattice.h000066400000000000000000000317371321422335000215470ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/Lattices/MaskedLattice.tcc000066400000000000000000000202031321422335000220530ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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. Array* arr = new Array; Bool isARef = This->getMaskSlice (*arr, section, removeDegenerateAxes); buffer = COWPtr > (arr, True, 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-2.4.1/lattices/Lattices/MaskedLatticeIterator.h000066400000000000000000000202251321422335000232470ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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); CountedPtr > itsMaskLattPtr; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-2.4.1/lattices/Lattices/MaskedLatticeIterator.tcc000066400000000000000000000114211321422335000235670ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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), 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); 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) { itsMaskLattPtr = CountedPtr > (mptr, False); } else { itsMaskLattPtr = mlattice.cloneML(); } } 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-2.4.1/lattices/Lattices/PagedArrIter.h000066400000000000000000000114061321422335000213350ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/Lattices/PagedArrIter.tcc000066400000000000000000000062051321422335000216600ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/Lattices/PagedArray.h000066400000000000000000000650631321422335000210530ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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. #ifdef AIPS_CXX11 extern template class PagedArray; extern template class PagedArray; #endif } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-2.4.1/lattices/Lattices/PagedArray.tcc000066400000000000000000000371271321422335000213750ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 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-2.4.1/lattices/Lattices/PixelCurve1D.cc000066400000000000000000000123641321422335000214410ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/Lattices/RebinLattice.h000066400000000000000000000141341321422335000213720ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/Lattices/RebinLattice.tcc000066400000000000000000000247141321422335000217210ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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. #ifdef AIPS_CXX11 extern template class SubLattice; extern template class SubLattice; #endif } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-2.4.1/lattices/Lattices/SubLattice.tcc000066400000000000000000000422301321422335000214040ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: SubLattice.tcc 21563 2015-02-16 07:05:15Z gervandiepen $ #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-2.4.1/lattices/Lattices/TempLattice.h000066400000000000000000000257611321422335000212500ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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: CountedPtr > itsImpl; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-2.4.1/lattices/Lattices/TempLattice.tcc000066400000000000000000000110351321422335000215570ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/Lattices/TempLatticeImpl.h000066400000000000000000000224441321422335000220650ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id: TempLatticeImpl.h 20739 2009-09-29 01:15:15Z Malte.Marquarding $ #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 CountedPtr 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 (itsTablePtr != 0) itsTablePtr->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* itsTablePtr; mutable CountedPtr > itsLatticePtr; String itsTableName; mutable Bool itsIsClosed; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-2.4.1/lattices/Lattices/TempLatticeImpl.tcc000066400000000000000000000101771321422335000224070ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: TempLatticeImpl.tcc 20739 2009-09-29 01:15:15Z Malte.Marquarding $ #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() : itsTablePtr (0), itsIsClosed (False) { itsLatticePtr = new ArrayLattice; } template TempLatticeImpl::TempLatticeImpl (const TiledShape& shape, Int maxMemoryInMB) : itsTablePtr (0), itsIsClosed (False) { init (shape, Double(maxMemoryInMB)); } template TempLatticeImpl::TempLatticeImpl (const TiledShape& shape, Double maxMemoryInMB) : itsTablePtr (0), itsIsClosed (False) { init(shape, maxMemoryInMB); } template TempLatticeImpl::~TempLatticeImpl() { // Reopen to make sure that temporary table gets deleted. doReopen(); delete itsTablePtr; } 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); itsTablePtr = new Table (newtab, TableLock::PermanentLockingWait); itsLatticePtr = new PagedArray (shape, *itsTablePtr); } else { itsLatticePtr = new ArrayLattice (shape.shape()); } } template void TempLatticeImpl::tempClose() { if (itsTablePtr != 0 && isPaged()) { // Take care that table does not get deleted, otherwise we cannot reopen. itsTablePtr->unmarkForDelete(); delete itsTablePtr; itsTablePtr = 0; itsLatticePtr = 0; // CountedPtr does delete of pointer itsIsClosed = True; } } template void TempLatticeImpl::tempReopen() const { if (itsIsClosed && isPaged()) { itsTablePtr = new Table (itsTableName, TableLock(TableLock::PermanentLockingWait), Table::Update); itsLatticePtr = new PagedArray (*itsTablePtr); itsIsClosed = False; } if (itsTablePtr != 0) { itsTablePtr->markForDelete(); } } } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/lattices/Lattices/TileStepper.cc000066400000000000000000000341401321422335000214220ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/Lattices/TileStepper.h000066400000000000000000000270151321422335000212670ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/Lattices/TiledLineStepper.cc000066400000000000000000000427111321422335000224010ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/Lattices/TiledLineStepper.h000066400000000000000000000364161321422335000222500ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/Lattices/TiledShape.cc000066400000000000000000000201601321422335000212010ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations template class Vector; // // 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-2.4.1/lattices/Lattices/test/000077500000000000000000000000001321422335000176305ustar00rootroot00000000000000casacore-2.4.1/lattices/Lattices/test/CMakeLists.txt000066400000000000000000000011031321422335000223630ustar00rootroot00000000000000set (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) target_link_libraries (${test} casa_lattices) add_test (${test} ${CMAKE_SOURCE_DIR}/cmake/cmake_assay ./${test}) add_dependencies(check ${test}) endforeach (test) casacore-2.4.1/lattices/Lattices/test/dLattice.cc000066400000000000000000000116361321422335000216770ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { cout << x.getMesg() << endl << "FAIL" << endl; return 1; } cout << "OK" << endl; return 0; } // Local Variables: // compile-command: "gmake OPTLIB=1 dLattice" // End: casacore-2.4.1/lattices/Lattices/test/dPagedArray.cc000066400000000000000000000232401321422335000223230ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { cerr << x.getMesg() << endl; cout << "FAIL" << endl; return 1; } cout << "OK" << endl; return 0; } casacore-2.4.1/lattices/Lattices/test/dPagedArray.run000066400000000000000000000000771321422335000225450ustar00rootroot00000000000000#!/bin/sh $casa_checktool ./dPagedArray n1=16 n2=16 n3=2 n4=2 casacore-2.4.1/lattices/Lattices/test/tArrayLattice.cc000066400000000000000000001002321321422335000227050ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { caught = True; } AlwaysAssert(caught, AipsError); caught = False; try { al6ROIter.cubeCursor(); } catch (AipsError 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 (AipsError x) { caught = True; } AlwaysAssert(caught, AipsError); caught = False; try { al6Iter.cubeCursor(); } catch (AipsError 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 (AipsError x) { cerr << x.getMesg () << endl; cout << "FAIL" << endl; return 1; } cout << "OK" << endl; return 0; } casacore-2.4.1/lattices/Lattices/test/tCurvedLattice2D.cc000066400000000000000000000157041321422335000232560ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { cerr << "Caught exception: " << x.getMesg() << endl; cout << "FAIL" << endl; return 1; } cout << "OK" << endl; return 0; } casacore-2.4.1/lattices/Lattices/test/tExtendLattice.cc000066400000000000000000000212251321422335000230620ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { cerr << "Caught exception: " << x.getMesg() << endl; cout << "FAIL" << endl; return 1; } cout << "OK" << endl; return 0; } casacore-2.4.1/lattices/Lattices/test/tHDF5Iterator.cc000066400000000000000000001474151321422335000225370ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { if (!x.getMesg().contains("two non-degenerate")) { throw (AipsError (x.getMesg())); } } try { Cube temp(iter.cubeCursor()); throw(AipsError("tHDF5Iterator - " "cubeCursor worked where it should not have")); } catch (AipsError x) { if (!x.getMesg().contains("three non-degenerate")) { throw (AipsError (x.getMesg())); } } 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 (AipsError x) { if (!x.getMesg().contains("one non-degenerate")) { throw (AipsError (x.getMesg())); } } try { Cube temp(iter.cubeCursor()); throw(AipsError("tHDF5Iterator - " "cubeCursor worked where it should not have")); } catch (AipsError x) { if (!x.getMesg().contains("three non-degenerate")) { throw (AipsError (x.getMesg())); } } 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 (AipsError x) { if (!x.getMesg().contains("one non-degenerate")) { throw (AipsError (x.getMesg())); } } try { Matrix temp(iter.matrixCursor()); throw(AipsError("tHDF5Iterator - " "matrixCursor worked where it should not have")); } catch (AipsError x) { if (!x.getMesg().contains("two non-degenerate")) { throw (AipsError (x.getMesg())); } } 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 (AipsError x) { if (!x.getMesg().contains("one non-degenerate")) { throw (AipsError (x.getMesg())); } } try { Matrix temp(iter.matrixCursor()); throw(AipsError("tHDF5Iterator - " "matrixCursor worked where it should not have")); } catch (AipsError x) { if (!x.getMesg().contains("two non-degenerate")) { throw (AipsError (x.getMesg())); } } try { Cube temp(iter.cubeCursor()); throw(AipsError("tHDF5Iterator - " "cubeCursor worked where it should not have")); } catch (AipsError x) { if (!x.getMesg().contains("three non-degenerate")) { throw (AipsError (x.getMesg())); } } 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 (AipsError x) { if (!x.getMesg().contains("two non-degenerate")) { throw (AipsError (x.getMesg())); } } try { Cube temp(iter.cubeCursor()); throw(AipsError("tHDF5Iterator - " "cubeCursor worked where it should not have")); } catch (AipsError x) { if (!x.getMesg().contains("three non-degenerate")) { throw (AipsError (x.getMesg())); } } 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 (AipsError x) { if (!x.getMesg().contains("two non-degenerate")) { throw (AipsError (x.getMesg())); } } try { Cube temp(iter.cubeCursor()); throw(AipsError("tHDF5Iterator - " "cubeCursor worked where it should not have")); } catch (AipsError x) { if (!x.getMesg().contains("three non-degenerate")) { throw (AipsError (x.getMesg())); } } 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 (AipsError x) { if (!x.getMesg().contains("one non-degenerate")) { throw (AipsError (x.getMesg())); } } try { Cube temp(iter.cubeCursor()); throw(AipsError("tHDF5Iterator - " "cubeCursor worked where it should not have")); } catch (AipsError x) { if (!x.getMesg().contains("three non-degenerate")) { throw (AipsError (x.getMesg())); } } 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 (AipsError x) { if (!x.getMesg().contains("one non-degenerate")) { throw (AipsError (x.getMesg())); } } try { Matrix temp(iter.matrixCursor()); throw(AipsError("tHDF5Iterator - " "matrixCursor worked where it should not have")); } catch (AipsError x) { if (!x.getMesg().contains("two non-degenerate")) { throw (AipsError (x.getMesg())); } } 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 (AipsError x) { if (!x.getMesg().contains("one non-degenerate")) { throw (AipsError (x.getMesg())); } } try { Matrix temp(iter.matrixCursor()); throw(AipsError("tHDF5Iterator - " "matrixCursor worked where it should not have")); } catch (AipsError x) { if (!x.getMesg().contains("two non-degenerate")) { throw (AipsError (x.getMesg())); } } try { Cube temp(iter.cubeCursor()); throw(AipsError("tHDF5Iterator - " "cubeCursor worked where it should not have")); } catch (AipsError x) { if (!x.getMesg().contains("three non-degenerate")) { throw (AipsError (x.getMesg())); } } 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 (AipsError x) { cerr << "Caught exception: " << x.getMesg() << 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 (AipsError x) { cerr << "Caught exception: " << x.getMesg() << endl; cout << "FAIL" << endl; return 1; } cout << "OK" << endl; return 0; } casacore-2.4.1/lattices/Lattices/test/tHDF5Lattice.cc000066400000000000000000000172231321422335000223240ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { cerr << x.getMesg() << endl; return 1; } cout<< "OK"<< endl; return 0; } casacore-2.4.1/lattices/Lattices/test/tLatticeCache.cc000066400000000000000000000162311321422335000226370ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include #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 (AipsError 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 (AipsError 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 (AipsError x) {;} if (!ok) { throw (AipsError("setLattice forced failure did not work - this was unexpected")); } } } catch(AipsError x) { cerr << x.getMesg() << 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-2.4.1/lattices/Lattices/test/tLatticeIndexer.cc000066400000000000000000000415541321422335000232400ustar00rootroot00000000000000// 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { cerr << x.getMesg() << endl; cout << "FAIL" << endl; return 1; } } casacore-2.4.1/lattices/Lattices/test/tLatticeIterator.cc000066400000000000000000001475341321422335000234400ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { if (!x.getMesg().contains("two non-degenerate")) { throw (AipsError (x.getMesg())); } } try { Cube temp(iter.cubeCursor()); throw(AipsError("tLatticeIterator - " "cubeCursor worked where it should not have")); } catch (AipsError x) { if (!x.getMesg().contains("three non-degenerate")) { throw (AipsError (x.getMesg())); } } 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 (AipsError x) { if (!x.getMesg().contains("one non-degenerate")) { throw (AipsError (x.getMesg())); } } try { Cube temp(iter.cubeCursor()); throw(AipsError("tLatticeIterator - " "cubeCursor worked where it should not have")); } catch (AipsError x) { if (!x.getMesg().contains("three non-degenerate")) { throw (AipsError (x.getMesg())); } } 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 (AipsError x) { if (!x.getMesg().contains("one non-degenerate")) { throw (AipsError (x.getMesg())); } } try { Matrix temp(iter.matrixCursor()); throw(AipsError("tLatticeIterator - " "matrixCursor worked where it should not have")); } catch (AipsError x) { if (!x.getMesg().contains("two non-degenerate")) { throw (AipsError (x.getMesg())); } } 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 (AipsError x) { if (!x.getMesg().contains("one non-degenerate")) { throw (AipsError (x.getMesg())); } } try { Matrix temp(iter.matrixCursor()); throw(AipsError("tLatticeIterator - " "matrixCursor worked where it should not have")); } catch (AipsError x) { if (!x.getMesg().contains("two non-degenerate")) { throw (AipsError (x.getMesg())); } } try { Cube temp(iter.cubeCursor()); throw(AipsError("tLatticeIterator - " "cubeCursor worked where it should not have")); } catch (AipsError x) { if (!x.getMesg().contains("three non-degenerate")) { throw (AipsError (x.getMesg())); } } 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 (AipsError x) { if (!x.getMesg().contains("two non-degenerate")) { throw (AipsError (x.getMesg())); } } try { Cube temp(iter.cubeCursor()); throw(AipsError("tLatticeIterator - " "cubeCursor worked where it should not have")); } catch (AipsError x) { if (!x.getMesg().contains("three non-degenerate")) { throw (AipsError (x.getMesg())); } } 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 (AipsError x) { if (!x.getMesg().contains("two non-degenerate")) { throw (AipsError (x.getMesg())); } } try { Cube temp(iter.cubeCursor()); throw(AipsError("tLatticeIterator - " "cubeCursor worked where it should not have")); } catch (AipsError x) { if (!x.getMesg().contains("three non-degenerate")) { throw (AipsError (x.getMesg())); } } 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 (AipsError x) { if (!x.getMesg().contains("one non-degenerate")) { throw (AipsError (x.getMesg())); } } try { Cube temp(iter.cubeCursor()); throw(AipsError("tLatticeIterator - " "cubeCursor worked where it should not have")); } catch (AipsError x) { if (!x.getMesg().contains("three non-degenerate")) { throw (AipsError (x.getMesg())); } } 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 (AipsError x) { if (!x.getMesg().contains("one non-degenerate")) { throw (AipsError (x.getMesg())); } } try { Matrix temp(iter.matrixCursor()); throw(AipsError("tLatticeIterator - " "matrixCursor worked where it should not have")); } catch (AipsError x) { if (!x.getMesg().contains("two non-degenerate")) { throw (AipsError (x.getMesg())); } } 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 (AipsError x) { if (!x.getMesg().contains("one non-degenerate")) { throw (AipsError (x.getMesg())); } } try { Matrix temp(iter.matrixCursor()); throw(AipsError("tLatticeIterator - " "matrixCursor worked where it should not have")); } catch (AipsError x) { if (!x.getMesg().contains("two non-degenerate")) { throw (AipsError (x.getMesg())); } } try { Cube temp(iter.cubeCursor()); throw(AipsError("tLatticeIterator - " "cubeCursor worked where it should not have")); } catch (AipsError x) { if (!x.getMesg().contains("three non-degenerate")) { throw (AipsError (x.getMesg())); } } 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 (AipsError x) { cerr << "Caught exception: " << x.getMesg() << 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 (AipsError x) { cerr << "Caught exception: " << x.getMesg() << endl; cout << "FAIL" << endl; return 1; } cout << "OK" << endl; return 0; } casacore-2.4.1/lattices/Lattices/test/tLatticeLocker.cc000066400000000000000000000076001321422335000230530ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { cout << x.getMesg() << 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 (AipsError x) { cout << x.getMesg() << 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 (AipsError x) { cout << "Caught an exception: " << x.getMesg() << endl; return 1; } } return 0; // exit with success status } casacore-2.4.1/lattices/Lattices/test/tLatticePerf.cc000066400000000000000000000123211321422335000225240ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { cout << "Caught an exception: " << x.getMesg() << endl; return 1; } return 0; // exit with success status } casacore-2.4.1/lattices/Lattices/test/tLatticeStepper.cc000066400000000000000000000316621321422335000232630ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { if (!x.getMesg().contains("more axes than lattice")) { cout << x.getMesg() << 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 (AipsError x) { if (!x.getMesg().contains("> latticeShape")) { cout << x.getMesg() << 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 (AipsError x) { if (!x.getMesg().contains("cursorShape <=0")) { cout << x.getMesg() << 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 (AipsError x) { if (!x.getMesg().contains("makeAxisPath")){ cout << x.getMesg() << 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 (AipsError x) { if (!x.getMesg().contains("makeAxisPath")){ cout << x.getMesg() << 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 (AipsError 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 (AipsError 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 (AipsError 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 (AipsError 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 (AipsError 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 (AipsError 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 (AipsError 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 (AipsError x) { cout << x.getMesg() << endl << "FAIL" << endl; return 1; } cout << "OK" << endl; return 0; } // Local Variables: // compile-command: "gmake OPTLIB=1 tLatticeStepper" // End: casacore-2.4.1/lattices/Lattices/test/tLatticeUtilities.cc000066400000000000000000000140501321422335000236040ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 AipsError& x) { cout<< "FAIL"<< endl; cerr << x.getMesg() << 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 (AipsError x) { cerr << "Expected error = " << x.getMesg() << 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 (AipsError x) { cerr << x.getMesg() << endl; return 1; } cout<< "OK"<< endl; return 0; } casacore-2.4.1/lattices/Lattices/test/tPixelCurve1D.cc000066400000000000000000000132151321422335000226000ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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); pcurve3 = pcurve3; 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); pcurve4 = pcurve4; 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-2.4.1/lattices/Lattices/test/tPixelCurve1D.out000066400000000000000000000026331321422335000230240ustar00rootroot00000000000000[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-2.4.1/lattices/Lattices/test/tRebinLattice.cc000066400000000000000000000210501321422335000226660ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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-2.4.1/lattices/Lattices/test/tSubLattice.cc000066400000000000000000000426641321422335000223760ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError& 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 AipsError& x) { cerr << "Caught exception: " << x.getMesg() << endl; cout << "FAIL" << endl; return 1; } cout << "OK" << endl; return 0; } casacore-2.4.1/lattices/Lattices/test/tTempLattice.cc000066400000000000000000000063711321422335000225450ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { cerr << x.getMesg() << endl; cout << "FAIL" << endl; return 1; } cout << "OK" << endl; return 0; } casacore-2.4.1/lattices/Lattices/test/tTileStepper.cc000066400000000000000000000115361321422335000225710ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/Lattices/test/tTileStepper.out000066400000000000000000000236631321422335000230170ustar00rootroot00000000000000./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-2.4.1/lattices/Lattices/test/tTiledLineStepper.cc000066400000000000000000000115361321422335000235450ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/lattices/Lattices/test/tTiledLineStepper.out000066400000000000000000004262651321422335000240000ustar00rootroot00000000000000./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-2.4.1/lattices/Lattices/test/tTiledShape.cc000066400000000000000000000107151321422335000223510ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { cout << "Caught an exception: " << x.getMesg() << 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 (AipsError x) { cout << "Caught an exception: " << x.getMesg() << endl; return 1; } return 0; // exit with success status } casacore-2.4.1/lattices/lattices.dox000066400000000000000000000046001321422335000174250ustar00rootroot00000000000000//# 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: aips2-request@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-2.4.1/mainpage.dox000066400000000000000000000217001321422335000155660ustar00rootroot00000000000000//# 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: aips2-request@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-2.4.1/meas/000077500000000000000000000000001321422335000142165ustar00rootroot00000000000000casacore-2.4.1/meas/CMakeLists.txt000066400000000000000000000015021321422335000167540ustar00rootroot00000000000000# # CASA meas # add_library (casa_meas MeasUDF/PositionEngine.cc MeasUDF/PositionUDF.cc MeasUDF/EpochEngine.cc MeasUDF/EpochUDF.cc MeasUDF/DirectionUDF.cc MeasUDF/DirectionEngine.cc MeasUDF/Register.cc ) target_link_libraries (casa_meas casa_measures ${CASACORE_ARCH_LIBS}) 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/PositionUDF.h MeasUDF/PositionEngine.h MeasUDF/EpochUDF.h MeasUDF/EpochEngine.h MeasUDF/DirectionUDF.h MeasUDF/DirectionEngine.h MeasUDF/Register.h DESTINATION include/casacore/meas/MeasUDF ) install (FILES MeasUDF.h DESTINATION include/casacore/meas ) add_subdirectory (MeasUDF/test ${EXCL_ALL}) casacore-2.4.1/meas/MeasUDF.h000066400000000000000000000071021321422335000156130ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 directions, epochs, and positions. // // 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 emasures in tools that // deal with various table columns (e.g. TaQL, TablePlot, pyrap). // //# //# // } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/meas/MeasUDF/000077500000000000000000000000001321422335000154425ustar00rootroot00000000000000casacore-2.4.1/meas/MeasUDF/DirectionEngine.cc000066400000000000000000000542001321422335000210200ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include #include #include #include //#include #include namespace casacore { DirectionEngine::DirectionEngine() : itsNDim (-1), itsRefType (MDirection::N_Types), itsEpochEngine (0), itsPositionEngine (0) {} Bool DirectionEngine::isConstant() const { Bool isConst = itsConstants.size() > 0; if (isConst && itsEpochEngine) { isConst = itsEpochEngine->isConstant(); } if (isConst && itsPositionEngine) { isConst = itsPositionEngine->isConstant(); } return isConst; } void DirectionEngine::handleDirection (PtrBlock& args, uInt& argnr, Bool riseSet) { // Initialize to unknown reference type. itsRefType = MDirection::N_Types; // Normally directions must be given in an array, but a single one // can be 2 scalars. uInt nargnr = argnr+1; Bool asScalar = False; // A string means that object names (e.g. MOON) are given. if (args[argnr]->dataType() == TableExprNodeRep::NTString) { handleNames (args[argnr]); } else { if (args[argnr]->dataType() != TableExprNodeRep::NTDouble) { 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 a reference type is given. if (args.size() > nargnr && args[nargnr]->dataType() == TableExprNodeRep::NTString) { handleDirType (args[nargnr]); nargnr++; } // Process as two scalars or as array. if (asScalar) { handleScalars (args[argnr], args[argnr+1]); } else { handleDirArray (args[argnr]); } } // Skip the arguments handled. argnr = nargnr; // Set shape for constants. if (itsConstants.size() > 0) { if (itsConstants.size() > 1) { itsShape = itsConstants.shape(); } itsShape.prepend (IPosition(1,2)); } // Determine the output unit, shape, and ndim. if (riseSet) { itsUnit = "d"; } else { itsUnit = "rad"; } // Fill ndim if unknown and if shape is known. if (itsNDim < 0 && itsShape.size() > 0) { itsNDim = itsShape.size(); } } void DirectionEngine::handleDirType (TableExprNodeRep* operand) { if (operand->dataType() != TableExprNodeRep::NTString || operand->valueType() != TableExprNodeRep::VTScalar || !operand->isConstant()) { throw AipsError ("A direction type given in a MEAS function " "must be a constant scalar string"); } String str = operand->getString(0); Bool fnd = MDirection::getType (itsRefType, str); if (!fnd) { throw AipsError ("Unknown direction reference type " + str + " given in a MEAS function"); } } void DirectionEngine::handleScalars (TableExprNodeRep* e1, TableExprNodeRep* e2) { if (! (e1->isConstant() && e2->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); Unit u1 = e1->unit(); Unit u2 = e2->unit(); if (u1.empty()) u1 = "rad"; if (u2.empty()) u2 = "rad"; if (itsRefType == MDirection::N_Types) { itsRefType = MDirection::J2000; } itsConstants.resize (IPosition(1,1)); itsConstants.data()[0] = MDirection(Quantity(v1, u1), Quantity(v2, u2), itsRefType); } void DirectionEngine::handleNames (TableExprNodeRep* 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; iisReal() || operand->valueType() != TableExprNodeRep::VTArray) { throw AipsError ("A single double argument given as direction in a " "MEAS function must be a double array of values"); } // Set or convert the operand's unit to radian. TableExprNodeUnit::adaptUnit (operand, "rad") ; // Handle possibly given constants. if (operand->isConstant()) { handleConstant (operand); return; } // Try if the argument is a column. // If so, try to handle it as a TableMeasures column. const TableExprNodeArrayColumn* colNode = dynamic_cast(operand); 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); 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. MDirection::Types nodeRefType = MDirection::N_Types; if (! (measTmp.measDesc().isRefCodeVariable() || measTmp.measDesc().hasOffset())) { uInt refCode = measTmp.measDesc().getRefCode(); nodeRefType = static_cast(refCode); if (itsRefType != MDirection::N_Types && nodeRefType != itsRefType) { throw AipsError ("Given MDirection 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) { 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 == MDirection::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',DIRECTION)[0:3]'"); } } } if (itsMeasCol.isNull()) { if (itsRefType == MDirection::N_Types) { throw AipsError("No reference type given for a non-constant MEAS " "function direction argument"); } itsExprNode = operand; } } void DirectionEngine::handleConstant (TableExprNodeRep* operand) { AlwaysAssert (operand->valueType() != TableExprNodeRep::VTSet, AipsError); if (itsRefType == MDirection::N_Types) { itsRefType = MDirection::J2000; } TableExprNode node(operand); handleValues (node, 0, itsConstants); } void DirectionEngine::handleValues (TableExprNode& operand, const TableExprId& id, Array& directions) { Array values = operand.getArrayDouble(id); IPosition shape = values.shape(); if (shape[0] % 2 != 0) { throw AipsError ("Number of values in a direction in a MEAS function " "should be a multiple of 2"); } IPosition dirShape; if (shape[0] == 2 && shape.size() > 1) { dirShape = shape.getLast (shape.size() - 1); } else { dirShape = shape; dirShape[0] /= 2; } directions.resize (dirShape); Quantity q1(0, operand.unit()); Quantity q2(0, operand.unit()); Double* valVec = values.data(); MDirection* dirVec = directions.data(); for (uInt i=0; i 0 && itsNDim > 0) { itsNDim += ndim; } if (!shape.empty() && !itsShape.empty()) { itsShape.append (shape); } // Define the frame part, so it can be reset later. itsFrame.set (MEpoch()); } void DirectionEngine::setPositionEngine (PositionEngine& engine) { AlwaysAssert (itsPositionEngine == 0, AipsError); itsPositionEngine = &engine; uInt ndim = engine.ndim(); IPosition shape = engine.shape(); if (ndim > 0 && itsNDim > 0) { itsNDim += ndim; } if (!shape.empty() && !itsShape.empty()) { itsShape.append (shape); } // Define the frame part, so it can be reset later. itsFrame.set (MPosition()); } void DirectionEngine::setConverter (MDirection::Types toType) { MDirection::Ref ref(toType, itsFrame); itsConverter = MDirection::Convert (toType, ref); } Array DirectionEngine::getDirections (const TableExprId& id) { if (itsConstants.size() > 0) { return itsConstants; } if (!itsMeasCol.isNull()) { return itsMeasCol(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) { IPosition shape; if (res.size() > 1) { shape = res.shape(); } // 2 or 3 values per MDirection shape.prepend (IPosition(1, asDirCos ? 3:2)); if (eps.size() > 1) { shape.append (eps.shape()); } if (pos.size() > 1) { shape.append (pos.shape()); } out.resize (shape); double* outPtr = out.data(); uInt hIndex = 0; for (Array::const_contiter resIter = res.cbegin(); resIter != res.cend(); ++resIter, ++hIndex) { itsConverter.setModel (*resIter); 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 position. if (itsPositionEngine) { itsFrame.resetPosition (*posIter); } 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-2.4.1/meas/MeasUDF/DirectionEngine.h000066400000000000000000000163021321422335000206630ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef MEAS_DIRECTIONENGINE_H #define MEAS_DIRECTIONENGINE_H //# Includes #include #include #include #include #include #include #include #include namespace casacore { // // 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 ('APP', 'MOON', date(), [1e6m,1e6m,1e6m], '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 such functions return data with type double and unit radian. // // Futhermore, it is possible to get the rise/set date/time of a source given // the source direction, position on earth, and date. 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 is 1. // If the source is not visible at all, the rise time is 1 and set time is 0. // For example: // meas.riseset ('SUN', date(), 'WSRT') // Directions can be given like: // [x1,y1,z1,x2,y2,z2,...], fromRef // [lon1,lat1,lon2,lat2,...], fromRef // [lon1,lat1,lon2,lat2,...], [h1,h2,...], fromRef // where fromRef is the reference type optionally followed by _xxx // where xxx can be 'xyz' or 'll' to specify if the values are given as xyz // or as lon,lat. // If xxx is not given, it will be derived from the unit type of the values // (length means xyz, angle means lon,lat with default height is 0). // If xxx nor units are given, 3 values means xyz and 2 values means lon,lat. // If heights are also given, xxx must be 'll' if it is also given. // // A direction can also be a table column which usually knows its type. // It can also be an expression (e.g. DIRECTION[0,]) which also knows the type. //
        // // It makes it possible to handle measures in TaQL. // class DirectionEngine { public: DirectionEngine(); // Get the reference type. MDirection::Types refType() const { return itsRefType; } // Get the shape. const IPosition& shape() const { return itsShape; } // Get the dimensionality. Int ndim() const { return itsNDim; } // Tell if the expression is constant. Bool isConstant() const; // Get the unit. const Unit& unit() const { return itsUnit; } // 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 (PtrBlock& args, uInt& argnr, Bool riseSet); // Handle a direction reference type. void handleDirType (TableExprNodeRep* operand); // 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 (TableExprNodeRep* e1, TableExprNodeRep* e2); void handleNames (TableExprNodeRep* operand); void handleDirArray (TableExprNodeRep*& operand); void handleConstant (TableExprNodeRep* operand); 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. IPosition itsShape; Int itsNDim; Unit itsUnit; MeasFrame itsFrame; //# frame used by converter MDirection::Convert itsConverter; Array itsConstants; Vector itsH; //# diff for sun or moon MDirection::Types itsRefType; TableExprNode itsExprNode; ArrayMeasColumn itsMeasCol; EpochEngine* itsEpochEngine; PositionEngine* itsPositionEngine; }; } //end namespace #endif casacore-2.4.1/meas/MeasUDF/DirectionUDF.cc000066400000000000000000000126321321422335000202340ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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.handleDirType (operands()[0]); 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); // 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()); } 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-2.4.1/meas/MeasUDF/DirectionUDF.h000066400000000000000000000114671321422335000201030ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef MEAS_DIRECTIONUDF_H #define MEAS_DIRECTIONUDF_H //# Includes #include #include #include #include #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-2.4.1/meas/MeasUDF/EpochEngine.cc000066400000000000000000000277561321422335000201560ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include #include #include #include #include //#include #include namespace casacore { EpochEngine::EpochEngine() : itsNDim (-1), itsRefType (MEpoch::N_Types), itsPositionEngine (0) {} Bool EpochEngine::isConstant() const { Bool isConst = itsConstants.size() > 0; if (isConst && itsPositionEngine) { isConst = itsPositionEngine->isConstant(); } return isConst; } void EpochEngine::handleEpoch (PtrBlock& 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]); TableExprNodeRep::unlink (args[argnr]); args[argnr] = const_cast(dNode.getNodeRep())->link(); } // Check if the value is a date or double. if (!args[argnr]->isReal() && args[argnr]->dataType() != TableExprNodeRep::NTDate) { 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 (handleEpochType (args[nargnr], False)) { nargnr++; } } handleEpochArray (args[argnr]); // Skip the arguments handled. argnr = nargnr; // Determine the output unit, shape, and ndim. itsUnit = "s"; // Fill ndim if unknown and if shape is known. if (itsNDim < 0 && itsShape.size() > 0) { itsNDim = itsShape.size(); } if (itsShape.size() > 0) { // time has 1 value itsShape[0] = 1; } } Bool EpochEngine::handleEpochType (TableExprNodeRep* operand, Bool doThrow) { if (operand->dataType() != TableExprNodeRep::NTString || operand->valueType() != TableExprNodeRep::VTScalar || !operand->isConstant()) { if (doThrow) { throw AipsError ("An epoch type given in a MEAS function " "must be a constant scalar string"); } return False; } itsSidFrac = False; String str = operand->getString(0); str.upcase(); 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; } MEpoch::Types refType; Bool fnd = MEpoch::getType (refType, str); if (fnd) { itsRefType = refType; } else if (doThrow) { throw AipsError ("Unknown epoch reference type " + str + " given in a MEAS function"); } return fnd; } void EpochEngine::handleEpochArray (TableExprNodeRep* operand) { if ((!operand->isReal() && operand->dataType() != TableExprNodeRep::NTDate) || (operand->valueType() != TableExprNodeRep::VTScalar && operand->valueType() != TableExprNodeRep::VTArray)) { throw AipsError ("An epoch given in a MEAS function " "must be a numeric, string, or datetime scalar or array"); } 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); if (scaNode) { tabCol = &(scaNode->getColumn()); } else { const TableExprNodeArrayColumn* colNode = dynamic_cast(operand); 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); if (partNode) { colNode = partNode->getColumnNode(); tabCol = &(colNode->getColumn()); } } } if (tabCol) { // Try if the column contains measures. if (scaNode) { itsNDim = 0; } else { itsNDim = tabCol->ndimColumn(); itsShape = tabCol->shapeColumn(); } if (TableMeasDescBase::hasMeasures (*tabCol)) { TableMeasColumn measTmp(tabCol->table(), tabCol->columnDesc().name()); // Check the measure is an MEpoch. AlwaysAssert(measTmp.measDesc().type() == MEpoch::showMe(), AipsError); // Get and check the node's refType if it is fixed. MEpoch::Types nodeRefType = MEpoch::N_Types; if (! (measTmp.measDesc().isRefCodeVariable() || measTmp.measDesc().hasOffset())) { uInt refCode = measTmp.measDesc().getRefCode(); nodeRefType = static_cast(refCode); if (itsRefType != MEpoch::N_Types && nodeRefType != itsRefType) { throw AipsError ("Given MEpoch 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.epoch function. if (nodeRefType == MEpoch::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.epoch('UTC',TIMES)[0:3]'"); } } } if (itsMeasScaCol.isNull() && itsMeasArrCol.isNull()) { if (itsRefType == MEpoch::N_Types) { throw AipsError("No reference type given for a non-constant MEAS " "function epoch argument"); } // Convert expression from date to double if needed. itsExprNode = TableExprNode(operand); if (operand->dataType() == TableExprNodeRep::NTDate) { itsExprNode = mjd(itsExprNode); } } } void EpochEngine::handleConstant (TableExprNodeRep* operand) { // Get unit (default seconds). Unit unit = operand->unit(); if (unit.empty()) { unit = "s"; } // Get values (as doubles or dates). Array epochs; if (operand->isReal()) { epochs.reference (operand->getDoubleAS(0).array()); } else { unit = "s"; Array dates = operand->getDateAS(0).array(); epochs.resize (dates.shape()); for (uInt i=0; i epVec(epochs.reform(IPosition(1,epochs.size()))); itsConstants.resize (epochs.size()); for (uInt i=0; i 0 && itsNDim > 0) { itsNDim += ndim; } if (!shape.empty() && !itsShape.empty()) { itsShape.append (shape); } // 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; } Array 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 values = itsExprNode.getDoubleAS(id).array(); Array epochs(values.shape()); Unit unit = itsExprNode.unit(); if (unit.empty()) { unit = "s"; } Quantity q(0, unit); for (uInt i=0; i 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 resIter = res.cbegin(); resIter != res.cend(); ++resIter) { itsConverter.setModel (*resIter); for (Array::const_contiter posIter = pos.cbegin(); posIter != pos.cend(); ++posIter) { // Convert to desired reference type. if (itsPositionEngine) { itsFrame.resetPosition (*posIter); } 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-2.4.1/meas/MeasUDF/EpochEngine.h000066400000000000000000000124331321422335000200020ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef MEAS_EPOCHENGINE_H #define MEAS_EPOCHENGINE_H //# Includes #include #include #include #include #include #include #include #include namespace casacore { // // 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: EpochEngine(); // Get the reference type. MEpoch::Types refType() const { return itsRefType; } // Tell if the fraction has to be used for sidereal times. Bool sidFrac() const { return itsSidFrac; } // Get the shape. const IPosition& shape() const { return itsShape; } // Get the dimensionality. Int ndim() const { return itsNDim; } // Tell if the expression is constant. Bool isConstant() const; // Get the unit. const Unit& unit() const { return itsUnit; } // 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 (PtrBlock& args, uInt& argnr); // Handle a epoch reference type. // If the reference type is invalid, an exception is only thrown // if doThrow=True. In this way a string argument can // be an observatory name for a position. Bool handleEpochType (TableExprNodeRep* operand, Bool doThrow); // 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: void handleEpochArray (TableExprNodeRep* operand); void handleConstant (TableExprNodeRep* operand); //# Data members. IPosition itsShape; Int itsNDim; Unit itsUnit; Bool itsSidFrac; //# T = fraction for sidereal MeasFrame itsFrame; //# frame used by converter MEpoch::Convert itsConverter; Vector itsConstants; MEpoch::Types itsRefType; TableExprNode itsExprNode; ScalarMeasColumn itsMeasScaCol; ArrayMeasColumn itsMeasArrCol; PositionEngine* itsPositionEngine; }; } //end namespace #endif casacore-2.4.1/meas/MeasUDF/EpochUDF.cc000066400000000000000000000063471321422335000173600ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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.handleEpochType (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 (0, 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()); } 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-2.4.1/meas/MeasUDF/EpochUDF.h000066400000000000000000000071651321422335000172210ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef MEAS_EPOCHUDF_H #define MEAS_EPOCHUDF_H //# Includes #include #include #include #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-2.4.1/meas/MeasUDF/PositionEngine.cc000066400000000000000000000434731321422335000207160ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include #include #include #include #include #include namespace casacore { PositionEngine::PositionEngine() : itsNDim (-1), itsRefType (MPosition::N_Types), itsValueType (0) {} void PositionEngine::handlePosition (Int toValueType, PtrBlock& args, uInt& argnr) { // Types are unknown. itsRefType = MPosition::N_Types; 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 one // can be 2 or 3 scalars. 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 there is a node giving height(s). TableExprNodeRep* heightNode=0; if (args.size() > nargnr && args[nargnr]->isReal()) { heightNode = args[nargnr]; nargnr++; } // See if there is a reference type. if (args.size() > nargnr && args[nargnr]->dataType() == TableExprNodeRep::NTString) { handlePosType (args[nargnr]); nargnr++; } // Process as 2 or 3 scalars or as array. if (asScalar) { handleScalars (args[argnr], args[argnr+1], heightNode); } else { // Get the position arguments. if (heightNode) { // For time being, heights can only be given as constants. handlePosArray (args[argnr], heightNode); } else { handlePosArray (args[argnr]); } } } // Skip the arguments handled. argnr = nargnr; // Determine the output unit, shape, and ndim. itsOutUnit = "m"; if (toValueType == 2) { itsOutUnit = "rad"; // angles } if (itsConstants.size() > 0) { if (itsConstants.size() > 1) { itsShape = itsConstants.shape(); } itsShape.prepend (IPosition(1,3)); } // If returning height, we use one dimension less. if (toValueType == 1) { if (itsShape.size() > 0) { IPosition outShape = itsShape.getLast (itsShape.size() - 1); itsShape.resize (outShape.size()); itsShape = outShape; } } else if (itsShape.size() > 0) { // Set correct nr of output values per position. itsShape[0] = toValueType; } // Fill ndim if unknown and if shape is known. if (itsNDim < 0 && itsShape.size() > 0) { itsNDim = itsShape.size(); } } void PositionEngine::handlePosType (TableExprNodeRep* operand) { if (operand->dataType() != TableExprNodeRep::NTString || operand->valueType() != TableExprNodeRep::VTScalar || !operand->isConstant()) { throw AipsError ("A position type given in a MEAS function " "must be a constant scalar string"); } // Get value and possible suffix (value type). String str = operand->getString(0); str.upcase(); int lens = str.size(); if (lens > 3 && str.substr(lens-3,3) == "XYZ") { itsValueType = 3; str = str.substr(0,lens-3); } else if (lens > 2 && str.substr(lens-2,2) == "LL") { itsValueType = 2; str = str.substr(0,lens-2); } else if (lens > 6 && str.substr(lens-6,6) == "LONLAT") { itsValueType = 2; str = str.substr(0,lens-6); } else if (lens > 1 && str.substr(lens-1,1) == "H") { itsValueType = 1; str = str.substr(0,lens-1); } else if (lens > 6 && str.substr(lens-6,6) == "HEIGHT") { itsValueType = 1; str = str.substr(0,lens-6); } else { itsValueType = 0; } // Get reference type. if (! MPosition::getType (itsRefType, str)) { throw AipsError ("Unknown position reference type and/or suffix " + str + " given in a MEAS function"); } } void PositionEngine::makeDefaults (const Unit& unit) { // Check if the unit is length or angle. itsInUnit = unit; int valueType = 0; if (! itsInUnit.empty()) { Quantity q(1., itsInUnit); if (q.isConform ("m")) { valueType = 3; } else if (q.isConform ("rad")) { valueType = 2; } else { throw AipsError ("Invalid unit given for a position value" " in a MEAS function (no length or angle)"); } } // Use derived value type if not given. if (itsValueType == 0) { itsValueType = valueType; } else if (valueType > 0 && valueType != itsValueType) { throw AipsError ("Value unit " + unit.getName() + " mismatches type " " suffix for a position in a MEAS function"); } // Use default reference type if not given. if (itsValueType == 3) { if (itsRefType == MPosition::N_Types) itsRefType = MPosition::ITRF; if (itsInUnit.empty()) itsInUnit = "m"; } else { if (itsRefType == MPosition::N_Types) itsRefType = MPosition::WGS84; if (itsInUnit.empty()) itsInUnit = "rad"; } if (itsRefType == MPosition::N_Types || !(itsValueType == 2 || itsValueType == 3)) { throw AipsError ("A position in a MEAS function is given " "without a valid reference type or suffix or unit"); } } void PositionEngine::handleScalars (TableExprNodeRep* e1, TableExprNodeRep* e2, TableExprNodeRep* heightNode) { if (! (e1->isConstant() && e2->isConstant()) || (heightNode && ! (heightNode->isConstant() && heightNode->valueType() == TableExprNodeRep::VTScalar))) { throw AipsError ("Scalar values given as position in a MEAS function " "must be constant values"); } makeDefaults (e1->unit()); double vh = 0; double v1 = e1->getDouble(0); double v2 = e2->getDouble(0); Unit uh("m"); Unit u1 = e1->unit(); Unit u2 = e2->unit(); if (u1.empty()) u1 = itsInUnit; if (u2.empty()) u2 = itsInUnit; itsConstants.resize (IPosition(1,1)); if (heightNode) { vh = heightNode->getDouble(0); uh = heightNode->unit(); if (uh.empty()) uh = "m"; } itsConstants.data()[0] = makePosition(Quantity(vh, uh), Quantity(v1, u1), Quantity(v2, u2)); } MPosition PositionEngine::makePosition (const Quantity& qh, const Quantity& q1, const Quantity& q2) const { if (itsValueType == 3) { Unit m("m"); return MPosition (MVPosition(q1.getValue(m), q2.getValue(m), qh.getValue(m)), itsRefType); } return MPosition (qh, q1, q2, itsRefType); } void PositionEngine::handleObservatory (TableExprNodeRep* 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"); } // Use defaults for reference and value type if not given. makeDefaults (operand->unit()); // Set or convert the operands's unit to radian or meter. TableExprNodeUnit::adaptUnit (operand, itsInUnit); // 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); 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); 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(); nodeRefType = static_cast(refCode); if (itsRefType != MPosition::N_Types && nodeRefType != itsRefType) { throw AipsError ("Given MPosition 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) { 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 (TableExprNodeRep* anglesNode, TableExprNodeRep* 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.size() %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 (IPosition(1, hVec.size())); for (uInt i=0; ivalueType() != TableExprNodeRep::VTSet, AipsError); TableExprNode node(operand); handleValues (node, 0, itsConstants); } void PositionEngine::handleValues (TableExprNode& operand, const TableExprId& id, Array& positions) { Array values = operand.getArrayDouble(id); const IPosition& shape = values.shape(); if (shape[0] % itsValueType != 0) { throw AipsError ("Number of values in a position in a MEAS function " "should be a multiple of " + String::toString(itsValueType)); } IPosition posShape; if (shape[0] == itsValueType && shape.size() > 1) { posShape = shape.getLast (shape.size() - 1); } else { posShape = shape; posShape[0] /= itsValueType; } positions.resize (posShape); Quantity qh(0, itsInUnit); Quantity q1(0, itsInUnit); Quantity q2(0, itsInUnit); if (itsValueType != 3) { qh = Quantity(0, "m"); } Double* dirVec = values.data(); MPosition* posVec = positions.data(); for (uInt i=0; i PositionEngine::getPositions (const TableExprId& id) { if (itsConstants.size() > 0) { return itsConstants; } if (!itsMeasCol.isNull()) { return itsMeasCol(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 #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, fromref) // meas.itrf (pos, fromref) // meas.wgs84 (pos, fromref) // For example, // meas.pos ('ITRF', [1e6m,1e6m,1e6m], '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. // Positions can be given like: // [x1,y1,z1,x2,y2,z2,...], fromRef // [lon1,lat1,lon2,lat2,...], fromRef // [lon1,lat1,lon2,lat2,...], [h1,h2,...], fromRef // where fromRef is the reference type optionally followed by _xxx // where xxx can be 'xyz' or 'll' to specify if the values are given as xyz // or as lon,lat. // If xxx is not given, it will be derived from the unit type of the values // (length means xyz, angle means lon,lat with default height is 0). // If xxx nor units are given, 3 values means xyz and 2 values means lon,lat. // If heights are also given, xxx must be 'll' if it is also given. // // A position can also be a table column which usually knows its type. // It can also be an expression (e.g. POSITION[0,]) which also knows the type. //
        // // It makes it possible to handle measures in TaQL. // class PositionEngine { public: PositionEngine(); // Get the reference type. MPosition::Types refType() const { return itsRefType; } // 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 shape. const IPosition& shape() const { return itsShape; } // Get the dimensionality. Int ndim() const { return itsNDim; } // Tell if the expression is constant. Bool isConstant() const { return itsConstants.size() > 0; } // Get the unit. const Unit& unit() const { return itsOutUnit; } // 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, PtrBlock& args, uInt& argnr); // Handle a position reference type and optional suffix. void handlePosType (TableExprNodeRep* operand); private: void makeDefaults (const Unit& unit); // Make an MPosition from xyz or angles,height. MPosition makePosition (const Quantity& qh, const Quantity& q1, const Quantity& q2) const; void handleScalars (TableExprNodeRep* e1, TableExprNodeRep* e2, TableExprNodeRep* heightNode); void handleObservatory (TableExprNodeRep* operand); void handlePosArray (TableExprNodeRep*& operand); void handlePosArray (TableExprNodeRep* angles, TableExprNodeRep* height); void handleConstant (TableExprNodeRep* operand); void handleValues (TableExprNode& operand, const TableExprId& id, Array& positions); //# Data members. IPosition itsShape; Int itsNDim; Unit itsInUnit; Unit itsOutUnit; CountedPtr itsConvert; Array itsConstants; MPosition::Types itsRefType; Int itsValueType; //# 0=default, 1=xyz, 2=angles, 3=height TableExprNode itsExprNode; ArrayMeasColumn itsMeasCol; }; } //end namespace #endif casacore-2.4.1/meas/MeasUDF/PositionUDF.cc000066400000000000000000000101641321422335000201160ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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::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::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 MEAS.POS function"); } // Get the 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 == ITRFH) { itsRefType = MPosition::ITRF; itsValueType = 1; } else if (itsType == WGSXYZ) { 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.handlePosType (operands()[0]); 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 (itsEngine.shape()); } } else { setNDim (itsEngine.ndim()); } setUnit (itsEngine.unit().getName()); setConstant (itsEngine.isConstant()); } 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-2.4.1/meas/MeasUDF/PositionUDF.h000066400000000000000000000075261321422335000177700ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef MEAS_POSITIONUDF_H #define MEAS_POSITIONUDF_H //# Includes #include #include #include #include #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, ITRFLL, ITRFH, WGSXYZ, WGSLL, WGSH}; explicit PositionUDF (FuncType); // Function to create an object. static UDFBase* makePOS (const String&); static UDFBase* makeITRFXYZ (const String&); static UDFBase* makeITRFLL (const String&); static UDFBase* makeITRFH (const String&); static UDFBase* makeWGSXYZ (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-2.4.1/meas/MeasUDF/Register.cc000066400000000000000000000247341321422335000175470ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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.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.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); } 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 << endl; os << TaQLShow::showMeasTypes ("direction"); } } 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); } 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); } if (os.str().empty()) { os << type << " is an unknown meas subtype; use pos(ition), epoch or dir(ection)" << 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-2.4.1/meas/MeasUDF/Register.h000066400000000000000000000043341321422335000174030ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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); }; } #endif casacore-2.4.1/meas/MeasUDF/test/000077500000000000000000000000001321422335000164215ustar00rootroot00000000000000casacore-2.4.1/meas/MeasUDF/test/CMakeLists.txt000066400000000000000000000004001321422335000211530ustar00rootroot00000000000000set (tests tmeas ) foreach (test ${tests}) add_executable (${test} ${test}.cc) target_link_libraries (${test} casa_meas) add_test (${test} ${CMAKE_SOURCE_DIR}/cmake/cmake_assay ./${test}) add_dependencies(check ${test}) endforeach (test) casacore-2.4.1/meas/MeasUDF/test/tmeas.cc000066400000000000000000000026761321422335000200540ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include #include using namespace casacore; int main() { HelpMeasUDF::showFuncsPosition (std::cout, False); HelpMeasUDF::showFuncsEpoch (std::cout, False); HelpMeasUDF::showFuncsDirection (std::cout, False); } casacore-2.4.1/meas/MeasUDF/test/tmeas.out000066400000000000000000000214631321422335000202710ustar00rootroot00000000000000Position 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 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-2.4.1/meas/MeasUDF/test/tmeas.run000077500000000000000000000015561321422335000202720ustar00rootroot00000000000000#!/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(date(meas.riseset(['SUN'],1jan10+[0:366],[5d0m, 52d0m])) != transpose(array(date(1jan2010+[0:366]),2,366)))" # Show the rise/set times for the year. $casa_checktool ../../../tables/apps/taql "transpose(time(meas.riseset(['SUN'],1jan10+[0:366],[4d53m22, 52d22m26])))" casacore-2.4.1/meas/meas.dox000066400000000000000000000027631321422335000156670ustar00rootroot00000000000000//# 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: aips2-request@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-2.4.1/measures/000077500000000000000000000000001321422335000151155ustar00rootroot00000000000000casacore-2.4.1/measures/CMakeLists.txt000066400000000000000000000103641321422335000176610ustar00rootroot00000000000000# # 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/TableMeas_tmpl.cc TableMeasures/TableMeasColumn.cc TableMeasures/TableMeasDescBase.cc TableMeasures/TableMeasOffsetDesc.cc TableMeasures/TableMeasRefDesc.cc TableMeasures/TableMeasType.cc TableMeasures/TableMeasValueDesc.cc TableMeasures/TableQuantumDesc.cc ) target_link_libraries (casa_measures casa_tables casa_scimath ${CASACORE_ARCH_LIBS}) add_subdirectory (apps) install ( TARGETS casa_measures 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 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 Measures.h TableMeasures.h DESTINATION include/casacore/measures ) add_subdirectory (Measures/test ${EXCL_ALL}) add_subdirectory (TableMeasures/test ${EXCL_ALL}) casacore-2.4.1/measures/Measures.h000066400000000000000000001160061321422335000170560ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/measures/Measures/000077500000000000000000000000001321422335000167015ustar00rootroot00000000000000casacore-2.4.1/measures/Measures/Aberration.cc000066400000000000000000000213221321422335000212760ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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); } CountedPtr > 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); } CountedPtr > 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-2.4.1/measures/Measures/Aberration.h000066400000000000000000000131011321422335000211340ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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-2.4.1/measures/Measures/EarthField.cc000066400000000000000000000167371321422335000212350ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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(C::pi_2 - posv(2)); clat = sin(C::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-2.4.1/measures/Measures/EarthField.h000066400000000000000000000165031321422335000210660ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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-2.4.1/measures/Measures/EarthMagneticMachine.cc000066400000000000000000000233651321422335000232210ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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-2.4.1/measures/Measures/EarthMagneticMachine.h000066400000000000000000000211221321422335000230500ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef MEASURES_EARTHMAGNETICMACHINE_H #define MEASURES_EARTHMAGNETICMACHINE_H //# Includes #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class MeasFrame; class MPosition; class MEpoch; template class Vector; // 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-2.4.1/measures/Measures/MBaseline.cc000066400000000000000000000201641321422335000210520ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# Includes #include #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; } uInt MBaseline::type() const { return Register(static_cast(0)); } void MBaseline::assure(const Measure &in) { if (in.type() != Register(static_cast(0))) { 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 (in.type() != Register(static_cast(0))) 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()); } uInt MBaseline::myType() { return Register(static_cast(0)); } 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-2.4.1/measures/Measures/MBaseline.h000066400000000000000000000171751321422335000207240ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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; // Reference enum Types (included originally for gcc 2.95) typedef WHATEVER_SUN_TYPEDEF(MBaseline) Types Types; //# 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(); virtual uInt type() const; 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 my type (as Register) static uInt myType(); // 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-2.4.1/measures/Measures/MCBase.cc000066400000000000000000000105251321422335000203050ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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-2.4.1/measures/Measures/MCBaseline.cc000066400000000000000000000264251321422335000211630ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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]; MutexedInit MCBaseline::theirMutexedInit (MCBaseline::doFillState); //# Constructors MCBaseline::MCBaseline() : measMath() { fillState(); } //# 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 namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class MCBaseline; class String; template class Vector; //# 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]; // Mutex for thread-safety. static MutexedInit theirMutexedInit; // Fill the global state in a thread-safe way. static void fillState() { theirMutexedInit.exec(); } //# 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 in a thread-safe way. static void doFillState (void*); }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/measures/Measures/MCDirection.cc000066400000000000000000000367341321422335000213650ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# Includes #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]; MutexedInit MCDirection::theirMutexedInit (MCDirection::doFillState); //# Constructors MCDirection::MCDirection() : MVPOS1(0), MVPOS2(0), MVPOS3(0), VEC61(0), VEC62(0), VEC63(0), measMath() { fillState(); } //# 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); 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); in = *MVPOS1; in.adjust(); // 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() { fillState(); return MCBase::showState(MCDirection::FromTo_p[0], MDirection::N_Types, MCDirection::N_Routes, MCDirection::ToRef_p); } void MCDirection::doFillState (void*) { MDirection::checkMyTypes(); MCBase::makeState(FromTo_p[0], MDirection::N_Types, N_Routes, ToRef_p); } } //# NAMESPACE CASACORE - END casacore-2.4.1/measures/Measures/MCDirection.h000066400000000000000000000140001321422335000212050ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #ifndef MEASURES_MCDIRECTION_H #define MEASURES_MCDIRECTION_H //# Includes #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class MCDirection; class MVPosition; class String; template class Vector; //# 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]; // Mutex for thread-safety. static MutexedInit theirMutexedInit; // Fill the global state in a thread-safe way. static void fillState() { theirMutexedInit.exec(); } //# 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 in a thread-safe way. static void doFillState (void*); }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/measures/Measures/MCDoppler.cc000066400000000000000000000075651321422335000210520ustar00rootroot00000000000000//# MCDoppler.cc: MDoppler conversion routines //# Copyright (C) 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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]; MutexedInit MCDoppler::theirMutexedInit (MCDoppler::doFillState); //# Constructors MCDoppler::MCDoppler() { fillState(); } //# 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]; // Mutex for thread-safety. static MutexedInit theirMutexedInit; // Fill the global state in a thread-safe way. static void fillState() { theirMutexedInit.exec(); } //# 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 in a thread-safe way. static void doFillState (void*); }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/measures/Measures/MCEarthMagnetic.cc000066400000000000000000000316471321422335000221560ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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]; MutexedInit MCEarthMagnetic::theirMutexedInit (MCEarthMagnetic::doFillState); //# Constructors MCEarthMagnetic::MCEarthMagnetic() : MVPOS1(0), EFIELD(0), measMath() { fillState(); } //# 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() { fillState(); return MCBase::showState(MCEarthMagnetic::FromTo_p[0], MEarthMagnetic::N_Types, MCEarthMagnetic::N_Routes, MCEarthMagnetic::ToRef_p); } void MCEarthMagnetic::doFillState (void*) { MEarthMagnetic::checkMyTypes(); MCBase::makeState(FromTo_p[0], MEarthMagnetic::N_Types, N_Routes, ToRef_p); } } //# NAMESPACE CASACORE - END casacore-2.4.1/measures/Measures/MCEarthMagnetic.h000066400000000000000000000135601321422335000220120ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #ifndef MEASURES_MCEARTHMAGNETIC_H #define MEASURES_MCEARTHMAGNETIC_H //# Includes #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class MCEarthMagnetic; class MVPosition; class EarthField; class String; template class Vector; //# 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]; // Mutex for thread-safety. static MutexedInit theirMutexedInit; // Fill the global state in a thread-safe way. static void fillState() { theirMutexedInit.exec(); } //# 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 in a thread-safe way. static void doFillState (void*); }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/measures/Measures/MCEpoch.cc000066400000000000000000000175701321422335000205000ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# Includes #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]; MutexedInit MCEpoch::theirMutexedInit (MCEpoch::doFillState); //# Constructors MCEpoch::MCEpoch() : NUTATFROM(0), NUTATTO(0) { fillState(); } //# 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())/C::_2pi; } 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() { fillState(); return MCBase::showState(MCEpoch::FromTo_p[0], MEpoch::N_Types, MCEpoch::N_Routes, MCEpoch::ToRef_p); } void MCEpoch::doFillState (void*) { MEpoch::checkMyTypes(); MCBase::makeState(FromTo_p[0], MEpoch::N_Types, N_Routes, ToRef_p); } } //# NAMESPACE CASACORE - END casacore-2.4.1/measures/Measures/MCEpoch.h000066400000000000000000000125151321422335000203340ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #ifndef MEASURES_MCEPOCH_H #define MEASURES_MCEPOCH_H //# Includes #include #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]; // Mutex for thread-safety. static MutexedInit theirMutexedInit; // Fill the global state in a thread-safe way. static void fillState() { theirMutexedInit.exec(); } //# 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 in a thread-safe way. static void doFillState (void*); }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/measures/Measures/MCFrame.cc000066400000000000000000000405741321422335000204740ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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-2.4.1/measures/Measures/MCFrame.h000066400000000000000000000153311321422335000203270ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/measures/Measures/MCFrequency.cc000066400000000000000000000243621321422335000214000ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# Includes #include #include #include #include #include #include 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]; MutexedInit MCFrequency::theirMutexedInit (MCFrequency::doFillState); //# Constructors MCFrequency::MCFrequency() : MVPOS1(0), MVDIR1(0), ABERFROM(0), ABERTO(0) { fillState(); } //# 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; ioperator()(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); 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); 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: *MVPOS1 = MVPosition(MeasTable::velocityLSRGal(0)); 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: *MVPOS1 = MVPosition(MeasTable::velocityLSRGal(0)); 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: *MVPOS1 = MVPosition(MeasTable::velocityLSRK(0)); 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: *MVPOS1 = MVPosition(MeasTable::velocityLSRK(0)); 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: *MVPOS1 = MVPosition(MeasTable::velocityLGROUP(0)); 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: *MVPOS1 = MVPosition(MeasTable::velocityLGROUP(0)); 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: *MVPOS1 = MVPosition(MeasTable::velocityCMB(0)); 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: *MVPOS1 = MVPosition(MeasTable::velocityCMB(0)); 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() { fillState(); return MCBase::showState(MCFrequency::FromTo_p[0], MFrequency::N_Types, MCFrequency::N_Routes, MCFrequency::ToRef_p); } void MCFrequency::doFillState (void*) { MFrequency::checkMyTypes(); MCBase::makeState(FromTo_p[0], MFrequency::N_Types, N_Routes, ToRef_p); } } //# NAMESPACE CASACORE - END casacore-2.4.1/measures/Measures/MCFrequency.h000066400000000000000000000126661321422335000212460ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #ifndef MEASURES_MCFREQUENCY_H #define MEASURES_MCFREQUENCY_H //# Includes #include #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]; // Mutex for thread-safety. static MutexedInit theirMutexedInit; // Fill the global state in a thread-safe way. static void fillState() { theirMutexedInit.exec(); } //# 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 in a thread-safe way. static void doFillState (void*); }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/measures/Measures/MCPosition.cc000066400000000000000000000114221321422335000212340ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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]; MutexedInit MCPosition::theirMutexedInit (MCPosition::doFillState); //# Constructors MCPosition::MCPosition() : DVEC1(0) { fillState(); } //# 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) ? C::pi_2 : - C::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() { fillState(); return MCBase::showState(MCPosition::FromTo_p[0], MPosition::N_Types, MCPosition::N_Routes, MCPosition::ToRef_p); } void MCPosition::doFillState (void*) { MPosition::checkMyTypes(); MCBase::makeState(FromTo_p[0], MPosition::N_Types, N_Routes, ToRef_p); } } //# NAMESPACE CASACORE - END casacore-2.4.1/measures/Measures/MCPosition.h000066400000000000000000000113371321422335000211030ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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; template class Vector; //# 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]; // Mutex for thread-safety. static MutexedInit theirMutexedInit; // Fill the global state in a thread-safe way. static void fillState() { theirMutexedInit.exec(); } //# 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 in a thread-safe way. static void doFillState (void*); }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/measures/Measures/MCRadialVelocity.cc000066400000000000000000000237171321422335000223550ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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]; MutexedInit MCRadialVelocity::theirMutexedInit (MCRadialVelocity::doFillState); //# Constructors MCRadialVelocity::MCRadialVelocity() : MVPOS1(0), MVDIR1(0), ABERFROM(0), ABERTO(0) { fillState(); } //# 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(C::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(C::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() { fillState(); return MCBase::showState(MCRadialVelocity::FromTo_p[0], MRadialVelocity::N_Types, MCRadialVelocity::N_Routes, MCRadialVelocity::ToRef_p); } void MCRadialVelocity::doFillState (void*) { MRadialVelocity::checkMyTypes(); MCBase::makeState(FromTo_p[0], MRadialVelocity::N_Types, N_Routes, ToRef_p); } } //# NAMESPACE CASACORE - END casacore-2.4.1/measures/Measures/MCRadialVelocity.h000066400000000000000000000130011321422335000222000ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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]; // Mutex for thread-safety. static MutexedInit theirMutexedInit; // Fill the global state in a thread-safe way. static void fillState() { theirMutexedInit.exec(); } //# 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 in a thread-safe way. static void doFillState (void*); }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/measures/Measures/MConvertBase.cc000066400000000000000000000031631321422335000215430ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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-2.4.1/measures/Measures/MConvertBase.h000066400000000000000000000163641321422335000214140ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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-2.4.1/measures/Measures/MCuvw.cc000066400000000000000000000415461321422335000202630ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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]; MutexedInit MCuvw::theirMutexedInit (MCuvw::doFillState); //# Constructors MCuvw::MCuvw() : measMath() { fillState(); } //# 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 namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class MCuvw; class String; template class Vector; //# 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]; // Mutex for thread-safety. static MutexedInit theirMutexedInit; // Fill the global state in a thread-safe way. static void fillState() { theirMutexedInit.exec(); } //# 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 in a thread-safe way. static void doFillState (void*); }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/measures/Measures/MDirection.cc000066400000000000000000000327121321422335000212520ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# Includes #include #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 == "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; } uInt MDirection::type() const { return Register(static_cast(0)); } void MDirection::assure(const Measure &in) { if (in.type() != Register(static_cast(0))) { 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 (in.type() != Register(static_cast(0))) 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()); } uInt MDirection::myType() { return Register(static_cast(0)); } 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-2.4.1/measures/Measures/MDirection.h000066400000000000000000000354131321422335000211150ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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; // Reference enum Types (included originally for gcc 2.95) typedef WHATEVER_SUN_TYPEDEF(MDirection) Types Types; //# 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 name of a moving source (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(); // // Tell me your reference type (as Register()) // N.B. as defined in MDirection.cc, it does NOT return the type of an // instance, i.e. it just returns Register(static_cast(0)). virtual uInt type() const; // 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; // Get my type (as Register) // N.B. Being static, it does NOT return the type of an instance, i.e. use it // as MDirection::myType(), not md.myType(). static uInt myType(); // 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-2.4.1/measures/Measures/MDoppler.cc000066400000000000000000000161301321422335000207330ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# Includes #include #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; } uInt MDoppler::type() const { return Register(static_cast(0)); } void MDoppler::assure(const Measure &in) { if (in.type() != Register(static_cast(0))) { 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 (in.type() != Register(static_cast(0))) 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()); } uInt MDoppler::myType() { return Register(static_cast(0)); } 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-2.4.1/measures/Measures/MDoppler.h000066400000000000000000000220261321422335000205760ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #ifndef MEASURES_MDOPPLER_H #define MEASURES_MDOPPLER_H //# Includes #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 Vector; 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; // Reference enum Types (included originally for gcc 2.95) typedef WHATEVER_SUN_TYPEDEF(MDoppler) Types Types; //# 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(); virtual uInt type() const; 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 my type (as Register) static uInt myType(); // 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-2.4.1/measures/Measures/MEarthMagnetic.cc000066400000000000000000000221271321422335000220440ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# Includes #include #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; } uInt MEarthMagnetic::type() const { return Register(static_cast(0)); } void MEarthMagnetic::assure(const Measure &in) { if (in.type() != Register(static_cast(0))) { 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(0))) 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()); } uInt MEarthMagnetic::myType() { return Register(static_cast(0)); } 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-2.4.1/measures/Measures/MEarthMagnetic.h000066400000000000000000000223741321422335000217120ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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; // Reference enum Types (included originally for gcc 2.95) typedef WHATEVER_SUN_TYPEDEF(MEarthMagnetic) Types Types; //# 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(); virtual uInt type() const; 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; // Get my type (as Register) static uInt myType(); // 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-2.4.1/measures/Measures/MEpoch.cc000066400000000000000000000150571321422335000203730ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# Includes #include #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; } uInt MEpoch::type() const { return Register(static_cast(0)); } void MEpoch::assure(const Measure &in) { if (in.type() != Register(static_cast(0))) { 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 (in.type() != Register(static_cast(0))) 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; } uInt MEpoch::myType() { return Register(static_cast(0)); } Quantity MEpoch::get(const Unit &inunit) const { return (data.getTime().get(inunit)); } Measure *MEpoch::clone() const { return (new MEpoch(*this)); } } //# NAMESPACE CASACORE - END casacore-2.4.1/measures/Measures/MEpoch.h000066400000000000000000000174771321422335000202450ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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; // Reference enum Types (included originally for gcc 2.95) typedef WHATEVER_SUN_TYPEDEF(MEpoch) Types Types; //# 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(); virtual uInt type() const; 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 my type (as Register) static uInt myType(); // 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-2.4.1/measures/Measures/MFrequency.cc000066400000000000000000000217201321422335000212700ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# Includes #include #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; } uInt MFrequency::type() const { return Register(static_cast(0)); } void MFrequency::assure(const Measure &in) { if (in.type() != Register(static_cast(0))) { 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(0))) 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()); } uInt MFrequency::myType() { return Register(static_cast(0)); } 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) { 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-2.4.1/measures/Measures/MFrequency.h000066400000000000000000000272261321422335000211410ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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; // Reference enum Types (included originally for gcc 2.95) typedef WHATEVER_SUN_TYPEDEF(MFrequency) Types Types; //# 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(); virtual uInt type() const; 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 my type (as Register) static uInt myType(); // 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); // 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-2.4.1/measures/Measures/MPosition.cc000066400000000000000000000171711321422335000211400ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# Includes #include #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; } uInt MPosition::type() const { return Register(static_cast(0)); } void MPosition::assure(const Measure &in) { if (in.type() != Register(static_cast(0))) { 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 (in.type() != Register(static_cast(0))) 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()); } uInt MPosition::myType() { return Register(static_cast(0)); } 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-2.4.1/measures/Measures/MPosition.h000066400000000000000000000165021321422335000207770ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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; // Reference enum Types (included originally for gcc 2.95) typedef WHATEVER_SUN_TYPEDEF(MPosition) Types Types; //# 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(); virtual uInt type() const; 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 my type (as Register) static uInt myType(); // 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-2.4.1/measures/Measures/MRBase.cc000066400000000000000000000033101321422335000203160ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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-2.4.1/measures/Measures/MRBase.h000066400000000000000000000137521321422335000201730ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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-2.4.1/measures/Measures/MRadialVelocity.cc000066400000000000000000000204551321422335000222460ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# Includes #include #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; } uInt MRadialVelocity::type() const { return Register(static_cast(0)); } void MRadialVelocity::assure(const Measure &in) { if (in.type() != Register(static_cast(0))) { 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 (in.type() != Register(static_cast(0))) 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()); } uInt MRadialVelocity::myType() { return Register(static_cast(0)); } 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-2.4.1/measures/Measures/MRadialVelocity.h000066400000000000000000000244051321422335000221070ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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; // Reference enum Types (included originally for gcc 2.95) typedef WHATEVER_SUN_TYPEDEF(MRadialVelocity) Types Types; //# 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(); virtual uInt type() const; 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 my type (as Register) static uInt myType(); // 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-2.4.1/measures/Measures/MeasBase.h000066400000000000000000000112611321422335000205330ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #ifndef MEASURES_MEASBASE_H #define MEASURES_MEASBASE_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations template class Vector; //# 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-2.4.1/measures/Measures/MeasBase.tcc000066400000000000000000000112461321422335000210600ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/measures/Measures/MeasComet.cc000066400000000000000000000300051321422335000210630ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ //# 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) { 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) { 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) { 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) { 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::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; } } 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-2.4.1/measures/Measures/MeasComet.h000066400000000000000000000177431321422335000207430ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #ifndef MEASURES_MEASCOMET_H #define MEASURES_MEASCOMET_H //# Includes #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class MVRadialVelocity; class MVDirection; template class Vector; // 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(); 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; }; //# Inline Implementations } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/measures/Measures/MeasConvert.h000066400000000000000000000245441321422335000213110ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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-2.4.1/measures/Measures/MeasConvert.tcc000066400000000000000000000323051321422335000216250ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 = WHATEVER_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 = WHATEVER_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 = WHATEVER_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 = WHATEVER_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 = WHATEVER_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 = WHATEVER_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 = WHATEVER_TYPENAME M::MVType(val); } else { *locres = WHATEVER_TYPENAME M::MVType(Quantity(val,unit)); } return operator()(*locres); } template const M &MeasConvert::operator()(const Vector &val) { if (unit.empty()) *locres = WHATEVER_TYPENAME M::MVType(val); else *locres = WHATEVER_TYPENAME M::MVType(Quantum >(val,unit)); return operator()(*locres); } template const M &MeasConvert::operator()(const Quantum &val) { unit = val.getUnit(); *locres = WHATEVER_TYPENAME M::MVType(val); return operator()(*locres); } template const M &MeasConvert::operator()(const Quantum > &val) { unit = val.getUnit(); *locres = WHATEVER_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 = WHATEVER_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(*(WHATEVER_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(*(WHATEVER_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 = WHATEVER_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 = WHATEVER_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 = WHATEVER_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-2.4.1/measures/Measures/MeasData.cc000066400000000000000000000156601321422335000206770ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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-2.4.1/measures/Measures/MeasData.h000066400000000000000000000114731321422335000205370ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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-2.4.1/measures/Measures/MeasFrame.cc000066400000000000000000000340171321422335000210550ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# Includes #include #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 (in->type() == Register(static_cast(0))) { lock(locker); delete rep->epval; rep->epval = in->clone(); unlock(locker); makeEpoch(); } else if (in->type() == Register(static_cast(0))) { lock(locker); delete rep->posval; rep->posval = in->clone(); unlock(locker); makePosition(); } else if (in->type() == Register(static_cast(0))) { lock(locker); delete rep->dirval; rep->dirval = in->clone(); unlock(locker); makeDirection(); } else if (in->type() == Register(static_cast(0))) { 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-2.4.1/measures/Measures/MeasFrame.h000066400000000000000000000273201321422335000207160ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef MEASURES_MEASFRAME_H #define MEASURES_MEASFRAME_H //# Includes #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 Vector; 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-2.4.1/measures/Measures/MeasIERS.cc000066400000000000000000000362561321422335000205740ustar00rootroot00000000000000//# MeasIERS.cc: Interface to IERS tables //# Copyright (C) 1996-2003,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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# Includes #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 uInt MeasIERS::predicttime_reg = 0; uInt MeasIERS::notable_reg = 0; uInt MeasIERS::forcepredict_reg = 0; volatile Bool MeasIERS::needInit = True; Double MeasIERS::dateNow = 0.0; Vector MeasIERS::ldat[MeasIERS::N_Files][MeasIERS::N_Types]; Bool MeasIERS::msgDone = False; const String MeasIERS::tp[MeasIERS::N_Files] = {"IERSeop97", "IERSpredict"}; uInt MeasIERS::sizeNote = 0; uInt MeasIERS::nNote = 0; MeasIERS::CLOSEFUN *MeasIERS::toclose = 0; Mutex MeasIERS::theirMutex; //# Member functions Bool MeasIERS::get(Double &returnValue, MeasIERS::Files file, MeasIERS::Types type, Double date) { returnValue = 0.0; if (needInit) { ScopedMutexLock locker(theirMutex); if (needInit) { initMeas(); needInit = False; } } // 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) { const Vector& mjds = ldat[which][0]; if (mjds.empty() || ut < mjds[0] || ut >= mjds[mjds.size()-1]) { if (!msgDone) { LogIO os(LogOrigin("MeasIERS", String("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; } msgDone = True; } 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", String("initMeas(MeasIERS::Files)"), WHERE)); os << String("IERS table ") + tp[which] + " seems to be corrupted (time step not 1)" << LogIO::EXCEPTION; } } } } void MeasIERS::closeMeas() { ScopedMutexLock locker(theirMutex); needInit = True; 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/" }; 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 + '/'; } Bool found = False; 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 << String("Requested data table ") << name << String(" 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() != String("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-2.4.1/measures/Measures/MeasIERS.h000066400000000000000000000235351321422335000204320ustar00rootroot00000000000000//# MeasIERS.h: Interface to IERS tables //# Copyright (C) 1996,1997,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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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. static void closeTables(); // Close the set of IERS tables only 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 static volatile Bool needInit; // Current date static Double dateNow; // Read data (meas - predict) static Vector ldat[N_Files][N_Types]; // Message given static Bool msgDone; // 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; // Mutex for thread-safety. static Mutex theirMutex; }; //# Inline Implementations } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/measures/Measures/MeasJPL.cc000066400000000000000000000250651321422335000204530ustar00rootroot00000000000000//# MeasJPL.cc: Interface to JPL DE tables //# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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 (!initMeas(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 (initMeas(which)) { res = cn[which][what]; return True; } return False; } Bool MeasJPL::getConst(Double &res, MeasJPL::Files which, const String &nam) { if (initMeas(which)) { const TableRecord &tr = t[which].keywordSet(); if (tr.isDefined(nam)) { res = tr.asDouble(nam); return True; } } return False; } Bool MeasJPL::initMeas(MeasJPL::Files which) { if (needInit[which]) { ScopedMutexLock locker(theirMutex); if (needInit[which]) { if (!doInitMeas(which)) { return False; } needInit[which] = False; } } return True; } Bool 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", String("initMeas(MeasJPL::Files)"), WHERE)); os << String("Corrupted JPL table ") + tp[which] << LogIO::EXCEPTION; } return (! t[which].isNull()); } void MeasJPL::closeMeas() { 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. ScopedMutexLock 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; } } } volatile Bool MeasJPL::needInit[MeasJPL::N_Files] = {True, True}; 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]; Mutex MeasJPL::theirMutex; } //# NAMESPACE CASACORE - END casacore-2.4.1/measures/Measures/MeasJPL.h000066400000000000000000000173321321422335000203130ustar00rootroot00000000000000//# MeasJPL.h: Interface to JPL DE tables //# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 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 initMeas(MeasJPL::Files which); static Bool 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 // Measured data readable static volatile Bool needInit[N_Files]; // 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]; // // Mutex for thread-safety. static Mutex theirMutex; }; //# Inline Implementations } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/measures/Measures/MeasMath.cc000066400000000000000000000513411321422335000207130ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# Includes #include #include #include #include #include #include #include #include #include #include #include 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(C::pi_2 - info_p[LAT] , 2u, C::pi, 3u)); } void MeasMath::deapplyHADECtoAZEL(MVPosition &in) { getInfo(LAT); in = RotMatrix(Euler(C::pi_2 - info_p[LAT] , 2u, C::pi, 3u)) * in; } void MeasMath::applyHADECtoAZELGEO(MVPosition &in) { getInfo(LATGEO); in *= RotMatrix(Euler(C::pi_2 - info_p[LATGEO] , 2u, C::pi, 3u)); } void MeasMath::deapplyHADECtoAZELGEO(MVPosition &in) { getInfo(LATGEO); in = RotMatrix(Euler(C::pi_2 - info_p[LATGEO] , 2u, C::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]); 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]); 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(-C::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-2.4.1/measures/Measures/MeasMath.h000066400000000000000000000234171321422335000205600ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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-2.4.1/measures/Measures/MeasRef.h000066400000000000000000000150541321422335000204010ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #ifndef MEASURES_MEASREF_H #define MEASURES_MEASREF_H //# Includes #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 CountedPtr 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-2.4.1/measures/Measures/MeasRef.tcc000066400000000000000000000145411321422335000207230ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 = 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.null(); } 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 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 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 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 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-2.4.1/measures/Measures/MeasTable.cc000066400000000000000000006263241321422335000210620ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: MeasTable.cc 21521 2014-12-10 08:06:42Z gervandiepen $ //# 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 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; MutexedInit MeasTable::obsMutexedInit (MeasTable::doInitObservatories); Vector MeasTable::obsNams; Vector MeasTable::obsPos; Vector MeasTable::antResponsesPath; MutexedInit MeasTable::lineMutexedInit (MeasTable::doInitLines); Vector MeasTable::lineNams; Vector MeasTable::linePos; MutexedInit MeasTable::srcMutexedInit (MeasTable::doInitSources); Vector MeasTable::srcNams; Vector MeasTable::srcPos; MutexedInit MeasTable::igrfMutexedInit (MeasTable::doInitIGRF); Double MeasTable::dtimeIGRF = 0; Double MeasTable::firstIGRF = 0; std::vector > MeasTable::coefIGRF; std::vector > MeasTable::dIGRF; uInt MeasTable::iau2000_reg = 0; uInt MeasTable::iau2000a_reg = 0; Mutex MeasTable::theirMutex; //# Member functions Bool MeasTable::useIAU2000() { if (!MeasTable::iau2000_reg) { iau2000_reg = AipsrcValue::registerRC(String("measures.iau2000.b_use"), False); } return AipsrcValue::get(MeasTable::iau2000_reg); } Bool MeasTable::useIAU2000A() { if (!MeasTable::iau2000a_reg) { iau2000a_reg = AipsrcValue::registerRC(String("measures.iau2000.b_use2000a"), False); } return AipsrcValue::get(MeasTable::iau2000a_reg); } Double MeasTable:: precRate00(const uInt which) { static 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 Double bias[3] = { -0.041775*C::arcsec, -0.0068192*C::arcsec, -0.0146*C::arcsec}; static 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 volatile Bool needInit = True; static Polynomial polyArray[6]; 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} }; calcFundArg(needInit, polyArray, &FUND[0]); DebugAssert(which < 6, AipsError); return polyArray[which]; } const Polynomial &MeasTable::fundArg1950(uInt which) { static volatile Bool needInit = True; static Polynomial polyArray[6]; 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} }; calcFundArg(needInit, polyArray, &FUND[0]); DebugAssert(which < 6, AipsError); return polyArray[which]; } const Polynomial &MeasTable::fundArg2000(uInt which) { static volatile Bool needInit = True; static Polynomial polyArray[6]; 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} }; calcFundArg00(needInit, polyArray, &FUND[0]); DebugAssert(which < 6, AipsError); return polyArray[which]; } const Polynomial &MeasTable::planetaryArg2000(uInt which) { static volatile Bool needInit = True; static Polynomial polyArray[14]; 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 } }; calcPlanArg00(needInit, polyArray, &FUND[0]); DebugAssert(which < 14, AipsError); return polyArray[which]; } void MeasTable::calcFundArg(volatile Bool &need, Polynomial result[6], const Double coeff[6][4]) { if (need) { ScopedMutexLock locker(theirMutex); if (need) { 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); } } need = False; } } } void MeasTable::calcFundArg00(volatile Bool &need, Polynomial result[6], const Double coeff[6][5]) { if (need) { ScopedMutexLock locker(theirMutex); if (need) { 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); } } need = False; } } } void MeasTable::calcPlanArg00(volatile Bool &need, Polynomial result[14], const Double coeff[8][2]) { static const Double APA[3] = { 0.0, 0.02438175, 0.00000538691 }; if (need) { ScopedMutexLock locker(theirMutex); if (need) { 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]); } need = False; } } } 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]); } CountedPtr > MeasTable::mulSC(Double time, Double epsilon) { return theirMulSC.getArray (time, epsilon); } CountedPtr > MeasTable::mulSC2000A(Double time, Double epsilon) { return theirMulSC2000A.getArray (time, epsilon); } CountedPtr > 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]); } CountedPtr > MeasTable::mulSC1950(Double time, Double epsilon) { return theirMulSC1950.getArray (time, epsilon); } Double MeasTable::dPsiEps(uInt which, Double T) { // Note that use of msgDone is not thread-safe, but it is harmless if // the message accidently appears multiple times. static Bool msgDone = False; DebugAssert(which < 2, AipsError); Double r = 0; MeasIERS::Types type = (which==1 ? MeasIERS::dEps : MeasIERS::dPsi); if (!MeasIERS::get(r, MeasIERS::MEASURED, type, T)) { if (!msgDone) { msgDone = True; LogIO os(LogOrigin("MeasTable", String("dPsiEps(uInt, Double)"), WHERE)); os << LogIO::NORMAL3 << String("High precision nutation information not available.") << LogIO::POST; } } /// cout << "psieps " << r << endl; return (r * C::arcsec); } // Planetary data Vector MeasTable::Planetary(MeasTable::Types which, Double T) { Vector res(6); static volatile Bool needInit = True; static MeasJPL::Files fil(MeasJPL::DE200); static String tnam[2] = { "DE200", "DE405"}; if (needInit) { ScopedMutexLock locker(theirMutex); if (needInit) { uInt t; Aipsrc::find (t, String("measures.jpl.ephemeris"), 2, tnam, String("DE200")); fil = (MeasJPL::Files)t; needInit = False; } } if (!MeasJPL::get(res, fil, (MeasJPL::Types)which, MVEpoch(T))) { LogIO os(LogOrigin("MeasTable", String("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; } // Planetary constants Double MeasTable::Planetary(MeasTable::JPLconst what) { static volatile Bool needInit = True; static Double cn[MeasTable::N_JPLconst]; static MeasJPL::Files fil(MeasJPL::DE200); static String tnam[2] = { "DE200", "DE405"}; if (needInit) { ScopedMutexLock locker(theirMutex); if (needInit) { uInt t; Aipsrc::find (t, String("measures.jpl.ephemeris"), 2, tnam, String("DE200")); 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", String("initObservatories()"), WHERE)); os << "Cannot read table of Observatories" << LogIO::EXCEPTION; } Int N = t.nrow(); if (N<1) { LogIO os(LogOrigin("MeasTable", String("initObservatories()"), 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", String("initObservatories()"), 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() { MeasTable::initObservatories(); return MeasTable::obsNams; } Bool MeasTable::Observatory(MPosition &obs, const String &nam) { MeasTable::initObservatories(); 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) { MeasTable::initObservatories(); 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 } // Source data void MeasTable::initLines() { lineMutexedInit.exec(); } void MeasTable::doInitLines (void*) { 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", String("initLines()"), WHERE)); os << "Cannot read table of spectral Lines" << LogIO::EXCEPTION; } Int N = t.nrow(); if (N<1) { LogIO os(LogOrigin("MeasTable", String("initLines()"), 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() { MeasTable::initLines(); return MeasTable::lineNams; } Bool MeasTable::Line(MFrequency &obs, const String &nam) { MeasTable::initLines(); 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() { srcMutexedInit.exec(); } void MeasTable::doInitSources (void*) { 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", String("initSources()"), WHERE)); os << "Cannot read table of Sources" << LogIO::EXCEPTION; } Int N = t.nrow(); if (N<1) { LogIO os(LogOrigin("MeasTable", String("initSources()"), 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", String("initSources()"), 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() { MeasTable::initSources(); return MeasTable::srcNams; } Bool MeasTable::Source(MDirection &obs, const String &nam) { MeasTable::initSources(); 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) { MeasTable::initIGRF(); // 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() { igrfMutexedInit.exec(); } void MeasTable::doInitIGRF (void*) { Table t; TableRecord kws; ROTableRow row; 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", String("IGRF(Double)"), 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", String("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 volatile Bool needInit = True; static Polynomial polyArray[13]; 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} }; if (needInit) { ScopedMutexLock locker(theirMutex); if (needInit) { 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]); } } needInit = False; } } DebugAssert(which < 13, AipsError); return polyArray[which]; } // Derivative aber const Polynomial &MeasTable::aberArgDeriv(uInt which) { static volatile Bool needInit = True; static Polynomial polyArray[13]; if (needInit) { const Polynomial * polyArray_ptrs[13]; for (int i=0; i<13; i++) { polyArray_ptrs[i] = &aberArg(i); } ScopedMutexLock locker(theirMutex); if (needInit) { for (int i=0; i<13; i++) { polyArray[i] = polyArray_ptrs[i]->derivative(); } needInit = False; } } DebugAssert(which < 13, AipsError); return polyArray[which]; } const Polynomial &MeasTable::aber1950Arg(uInt which) { static volatile Bool needInit = True; static Polynomial polyArray[12]; 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} }; if (needInit) { ScopedMutexLock locker(theirMutex); if (needInit) { 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); } } needInit = False; } } DebugAssert(which < 12, AipsError); return polyArray[which]; } // Derivative aber1950 const Polynomial &MeasTable::aber1950ArgDeriv(uInt which) { static volatile Bool needInit = True; static Polynomial polyArray[12]; if (needInit) { const Polynomial * polyArray_ptrs[12]; for (int i=0; i<12; i++) { polyArray_ptrs[i] = &aber1950Arg(i); } ScopedMutexLock locker(theirMutex); if (needInit) { for (int i=0; i<12; i++) { polyArray[i] = polyArray_ptrs[i]->derivative(); } needInit = False; } } DebugAssert(which < 12, AipsError); return polyArray[which]; } 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]); } CountedPtr > MeasTable::mulAber(Double time, Double epsilon) { return theirMulAber.getArray (time, epsilon); } CountedPtr > MeasTable::mulAber1950(Double time, Double epsilon) { return theirMulAber1950.getArray (time, epsilon); } const Vector &MeasTable::mulSunAber(uInt which) { static volatile Bool needInit = True; static Vector argArray[17]; 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} }; if (needInit) { ScopedMutexLock locker(theirMutex); if (needInit) { 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; } } needInit = False; } } DebugAssert(which < 17, AipsError); return argArray[which]; } const Vector &MeasTable::mulEarthAber(uInt which) { static volatile Bool needInit = True; static Vector argArray[17]; 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} }; if (needInit) { ScopedMutexLock locker(theirMutex); if (needInit) { 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; } } needInit = False; } } DebugAssert(which < 17, AipsError); return argArray[which]; } const Vector &MeasTable::AberETerm(uInt which) { static volatile Bool needInit = True; static Vector termArray[2]; static const Double TERM[2][3] = { {-1.62557, -0.31919, -0.13843}, {+1.245, -1.580, -0.659} }; if (needInit) { ScopedMutexLock locker(theirMutex); if (needInit) { 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; } for (i=0; i<3; i++) { termArray[1](i) = TERM[1][i] * 1e-3; } needInit = False; } } DebugAssert(which < 2, AipsError); return termArray[which]; } // Diurnal Aberration factor Double MeasTable::diurnalAber(Double radius, Double T) { /// static Double res; /// res = C::_2pi * radius / MeasData::SECinDAY * /// MeasTable::UTtoST(T)/C::c; /// return res; return C::_2pi * radius / MeasData::SECinDAY * MeasTable::UTtoST(T)/C::c; } // LSR velocity (kinematical) const Vector &MeasTable::velocityLSRK(uInt which) { static volatile Bool needInit = True; static Vector argArray[2]; static const Double LSR[2][3] = { {0.0145021, -0.865863, 0.500071}, {0.00724658, -0.865985, 0.500018} }; if (needInit) { ScopedMutexLock locker(theirMutex); if (needInit) { 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]; } } needInit = False; } } DebugAssert(which < 2, AipsError); return argArray[which]; } // LSR velocity (dynamical) const Vector &MeasTable::velocityLSR(uInt which) { static volatile Bool needInit = True; static Vector argArray[2]; static const Double LSR[2][3] = { {-0.0385568, -0.881138, 0.471285}, {-0.0461164, -0.880664, 0.471491} }; if (needInit) { ScopedMutexLock locker(theirMutex); if (needInit) { 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]; } } needInit = False; } } DebugAssert(which < 2, AipsError); return argArray[which]; } // LSR velocity wrt galactic centre const Vector &MeasTable::velocityLSRGal(uInt which) { static volatile Bool needInit = True; static Vector argArray[2]; static const Double LSR[2][3] = { {0.494109, -0.44483, 0.746982}, {0.492728, -0.450347, 0.744585} }; if (needInit) { ScopedMutexLock locker(theirMutex); if (needInit) { 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]; } } needInit = False; } } DebugAssert(which < 2, AipsError); return argArray[which]; } // LGROUP velocity wrt bary center const Vector &MeasTable::velocityLGROUP(uInt which) { static volatile Bool needInit = True; static Vector argArray[2]; static const Double LGROUP[2][3] = { {0.593553979227, -0.177954636914, 0.784873124106}, {0.5953342407, -0.184600136022, 0.781984610866} }; if (needInit) { ScopedMutexLock locker(theirMutex); if (needInit) { 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]; } } needInit = False; } } DebugAssert(which < 2, AipsError); return argArray[which]; } // CMB velocity wrt bary center const Vector &MeasTable::velocityCMB(uInt which) { static volatile Bool needInit = True; static Vector argArray[2]; static const Double CMB[2][3] = { {-0.97176985257, 0.202393953108, -0.121243727187}, {-0.970024232022, 0.213247954272, -0.11652595972} }; if (needInit) { ScopedMutexLock locker(theirMutex); if (needInit) { 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]; } } needInit = False; } } DebugAssert(which < 2, AipsError); return argArray[which]; } // Earth and Sun position const Polynomial &MeasTable::posArg(uInt which) { static volatile Bool needInit = True; static Polynomial polyArray[12]; 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 }; if (needInit) { ScopedMutexLock locker(theirMutex); if (needInit) { 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); } } needInit = False; } } DebugAssert(which < 12, AipsError); return polyArray[which]; } // Derivative of Earth and Sun position polynomial const Polynomial &MeasTable::posArgDeriv(uInt which) { static volatile Bool needInit = True; static Polynomial polyArray[12]; if (needInit) { const Polynomial * polyArray_ptrs[12]; for (int i=0; i<12; i++) { polyArray_ptrs[i] = &posArg(i); } ScopedMutexLock locker(theirMutex); if (needInit) { for (int i=0; i<12; i++) { polyArray[i] = polyArray_ptrs[i]->derivative(); } needInit = False; } } DebugAssert(which < 12, AipsError); return polyArray[which]; } 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]); } CountedPtr > MeasTable::mulPosEarthXY(Double time, Double epsilon) { return theirMulPosEarthXY.getArray (time, epsilon); } CountedPtr > MeasTable::mulPosEarthZ(Double time, Double epsilon) { return theirMulPosEarthZ.getArray (time, epsilon); } CountedPtr > MeasTable::mulPosSunXY(Double time, Double epsilon) { return theirMulPosSunXY.getArray (time, epsilon); } CountedPtr > MeasTable::mulPosSunZ(Double time, Double epsilon) { return theirMulPosSunZ.getArray (time, epsilon); } const RotMatrix &MeasTable::posToRect() { static RotMatrix rot(Euler(+84381.4091 * C::arcsec, 1, -0.0930 * C::arcsec, 3)); return rot; } const RotMatrix &MeasTable::rectToPos() { static volatile Bool needInit = True; static RotMatrix rot; if (needInit) { ScopedMutexLock locker(theirMutex); if (needInit) { rot = MeasTable::posToRect(); rot.transpose(); needInit = False; } } 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 volatile Bool needInit = True; static RotMatrix rot; if (needInit) { ScopedMutexLock locker(theirMutex); if (needInit) { rot = MeasTable::frameBias00(); rot.transpose(); needInit = False;; } } 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) { static Bool msgDone = False; 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)) { if (!msgDone) { msgDone = True; LogIO os(LogOrigin("MeasTable", String("PolarMotion(Double)"), WHERE)); os << LogIO::NORMAL3 << String("High precision polar motion information not available.") << LogIO::POST; } } /// 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 volatile Bool needInit = True; static Int N = 0; static Double (*LEAP)[4]; if (needInit) { ScopedMutexLock locker(theirMutex); if (needInit) { Table t; ROTableRow row; TableRecord kws; 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", String("dUTC(Double)"), WHERE)); os << "Cannot read leap second table TAI_UTC" << LogIO::EXCEPTION; } N = t.nrow(); if (N < 35) { LogIO os(LogOrigin("MeasTable", String("dUTC(Double)"), WHERE)); os << "Leap second table TAI_UTC corrupted" << LogIO::EXCEPTION; } if (Time().modifiedJulianDay() - dt > 180) { LogIO os(LogOrigin("MeasTable", String("dUTC(Double)"), WHERE)); os << LogIO::SEVERE << String("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; } LEAP = (Double (*)[4])(new Double[4*N]); for (Int i=0; i < N; i++) { row.get(i); for (Int j=0; j < 4; j++) { LEAP[i][j] = *(rfp[j]); } } needInit = False; } } 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; } 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 volatile Bool needInit = True; static Polynomial stPoly(3); if (needInit) { ScopedMutexLock locker(theirMutex); if (needInit) { stPoly.setCoefficient(0, 24110.54841); stPoly.setCoefficient(1, 8640184.812866); stPoly.setCoefficient(2, 0.093104); stPoly.setCoefficient(3, -6.2e-6); needInit = False; } } return (stPoly((ut1-MeasData::MJD2000)/MeasData::JDCEN)); } Double MeasTable::GMST00(Double ut1, Double tt) { static volatile Bool needInit = True; static Polynomial stPoly(4); if (needInit) { ScopedMutexLock locker(theirMutex); if (needInit) { 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); needInit = False; } } return (stPoly((tt-MeasData::MJD2000)/MeasData::JDCEN) + MeasTable::ERA00(ut1)); } Double MeasTable::ERA00(Double ut1) { static volatile Bool needInit = True; static Polynomial stPoly(1); if (needInit) { ScopedMutexLock locker(theirMutex); if (needInit) { stPoly.setCoefficient(0, 0.7790572732640*C::_2pi); stPoly.setCoefficient(1, 0.00273781191135448*C::_2pi); needInit = False; } } ut1 -= MeasData::MJD2000; return MVAngle(stPoly(ut1)+ C::_2pi*fmod(ut1, 1.0))(0.0).radian(); } Double MeasTable::sprime00(Double tt) { return ((tt-MeasData::MJD2000)/MeasData::JDCEN * -47e-6 * C::arcsec); } Double MeasTable::GMUT0(Double gmst1) { static volatile Bool needInit = True; static Polynomial stPoly(3); if (needInit) { ScopedMutexLock locker(theirMutex); if (needInit) { stPoly.setCoefficient(0, -0.65830845056254866847); stPoly.setCoefficient(1, -235.90946916710752); stPoly.setCoefficient(2, -0.00000252822553597972); stPoly.setCoefficient(3, 0.0000000001679); needInit = False; } } return (stPoly((gmst1-MeasData::MJD2000-6713.)/MeasData::JDCEN)); } Double MeasTable::UTtoST(Double ut1) { static volatile Bool needInit = True; static Polynomial UTSTPoly(2); if (needInit) { ScopedMutexLock locker(theirMutex); if (needInit) { UTSTPoly.setCoefficient(0, 1.002737909350795); UTSTPoly.setCoefficient(1, +5.9006e-11); UTSTPoly.setCoefficient(2, -5.9e-15); needInit = False; } } return(UTSTPoly((ut1-MeasData::MJD2000)/MeasData::JDCEN)); } Double MeasTable::dUT1(Double utc) { static Bool msgDone = False; static Double res = 0; static Double checkT = -1e6; ScopedMutexLock locker(theirMutex); if ( !nearAbs(utc, checkT, 0.04)) { checkT = utc; if (!MeasIERS::get(res, MeasIERS::MEASURED, MeasIERS::dUT1, utc)) { if (!msgDone) { msgDone = True; LogIO os(LogOrigin("MeasTable", String("dUT1(Double)"), WHERE)); os << LogIO::NORMAL3 << String("High precision dUT1 information not available.") << LogIO::POST; } } } /// cout << "dutc1 " << res << endl; return res; } } //# NAMESPACE CASACORE - END casacore-2.4.1/measures/Measures/MeasTable.h000066400000000000000000000443401321422335000207140ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #ifndef MEASURES_MEASTABLE_H #define MEASURES_MEASTABLE_H //# Includes #include #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) static Bool useIAU2000(); // If IAU2000 model, do we use the high precision 2000A model? 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 CountedPtr > mulSC(Double time, Double epsilon); static CountedPtr > mulSC1950(Double time, Double epsilon); static CountedPtr > mulSC2000A(Double time, Double epsilon); static CountedPtr > 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 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 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 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 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 CountedPtr > mulAber(Double time, Double epsilon); static CountedPtr > 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 CountedPtr > mulPosEarthXY(Double time, Double epsilon); static CountedPtr > mulPosEarthZ (Double time, Double epsilon); static CountedPtr > mulPosSunXY (Double time, Double epsilon); static CountedPtr > 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 (void*); static void doInitLines (void*); static void doInitSources (void*); static void doInitIGRF (void*); // 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 void calcFundArg(volatile Bool &need, Polynomial result[6], const Double coeff[6][4]); static void calcFundArg00(volatile Bool &need, Polynomial result[6], const Double coeff[6][5]); static void calcPlanArg00(volatile Bool &need, Polynomial result[14], const Double coeff[8][2]); // //# Data // 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 MutexedInit obsMutexedInit; static Vector obsNams; static Vector obsPos; static Vector antResponsesPath; // // Spectral line table data // static MutexedInit lineMutexedInit; static Vector lineNams; static Vector linePos; // // Sources table data // static MutexedInit srcMutexedInit; static Vector srcNams; static Vector srcPos; // // IGRF data // static MutexedInit igrfMutexedInit; static Double dtimeIGRF; static Double firstIGRF; static Double lastIGRF; static Double time0IGRF; static Double timeIGRF; static std::vector > coefIGRF; static std::vector > dIGRF; // // Aipsrc registration (for speed) of use of iau2000 and if so // the 2000a version // static uInt iau2000_reg; static uInt iau2000a_reg; // // Mutex for thread-safety. static Mutex theirMutex; }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/measures/Measures/MeasTableMul.cc000066400000000000000000002604001321422335000215250ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id: MeasTable.h 21420 2014-03-19 09:18:51Z gervandiepen $ #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN MeasTableMul::MeasTableMul() : itsLastUsed (0) {} void MeasTableMul::clear() { itsTimes.resize (0); itsUsed.resize (0); itsArrays.resize (0); } CountedPtr > MeasTableMul::getArray (Double time, Double epsilon) { { // cache lookup must be thread-safe ScopedMutexLock 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 > arr(new Matrix(itsDefArray.shape())); *arr = itsDefArray; calc (*arr, time); { // cache insertion must also be thread-safe ScopedMutexLock 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-2.4.1/measures/Measures/MeasTableMul.h000066400000000000000000000213131321422335000213650ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id: MeasTable.h 21420 2014-03-19 09:18:51Z gervandiepen $ #ifndef MEASURES_MEASTABLEMUL_H #define MEASURES_MEASTABLEMUL_H //# Includes #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 // CountedPtr to avoid that in one thread a Matrix is // removed from the cache, while another thread is still using that Matrix. // This assumes that CountedPtr 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(); CountedPtr > getArray (Double time, Double epsilon); virtual void init() = 0; virtual void calc(Matrix&, Double time) = 0; protected: Mutex itsMutex; Int64 itsLastUsed; vector itsUsed; vector itsTimes; 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-2.4.1/measures/Measures/Measure.cc000066400000000000000000000042741321422335000206200ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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-2.4.1/measures/Measures/Measure.h000066400000000000000000000425301321422335000204570ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #ifndef MEASURES_MEASURE_H #define MEASURES_MEASURE_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class String; class Unit; class MeasValue; class MRBase; template class Quantum; template class Vector; // // 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; // Get the type (== Register() of derived Measure (faster than Strings) // All should have: // static uInt myType(); virtual uInt type() const = 0; // 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-2.4.1/measures/Measures/MeasureHolder.cc000066400000000000000000000367441321422335000217650ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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() : hold_p(), 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(), hold_p(), mvhold_p(0), convertmv_p(False) { if (other.hold_p.ptr()) hold_p.set(other.hold_p.ptr()->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.ptr()) { hold_p.set(other.hold_p.ptr()->clone()); } else { hold_p.clear(); } createMV(other.mvhold_p.nelements()); for (uInt i=0; iclone(); } } return *this; } //# Member Functions Bool MeasureHolder::isEmpty() const { return (!hold_p.ptr()); } Bool MeasureHolder::isMeasure() const { return (hold_p.ptr()); } Bool MeasureHolder::isMDirection() const { return (hold_p.ptr() && hold_p.ptr()->type() == MDirection::myType()); } Bool MeasureHolder::isMDoppler() const { return (hold_p.ptr() && hold_p.ptr()->type() == MDoppler::myType()); } Bool MeasureHolder::isMEpoch() const { return (hold_p.ptr() && hold_p.ptr()->type() == MEpoch::myType()); } Bool MeasureHolder::isMFrequency() const { return (hold_p.ptr() && hold_p.ptr()->type() == MFrequency::myType()); } Bool MeasureHolder::isMPosition() const { return (hold_p.ptr() && hold_p.ptr()->type() == MPosition::myType()); } Bool MeasureHolder::isMRadialVelocity() const { return (hold_p.ptr() && hold_p.ptr()->type() == MRadialVelocity::myType()); } Bool MeasureHolder::isMBaseline() const { return (hold_p.ptr() && hold_p.ptr()->type() == MBaseline::myType()); } Bool MeasureHolder::isMuvw() const { return (hold_p.ptr() && hold_p.ptr()->type() == Muvw::myType()); } Bool MeasureHolder::isMEarthMagnetic() const { return (hold_p.ptr() && hold_p.ptr()->type() == MEarthMagnetic::myType()); } const Measure &MeasureHolder::asMeasure() const { if (!hold_p.ptr()) { throw(AipsError("Empty MeasureHolder argument for asMeasure")); } return *hold_p.ptr(); } const MDirection &MeasureHolder::asMDirection() const { if (!hold_p.ptr() || !isMDirection()) { throw(AipsError("Empty or wrong MeasureHolder for asMDirection")); } return dynamic_cast(*hold_p.ptr()); } const MDoppler &MeasureHolder::asMDoppler() const { if (!hold_p.ptr() || !isMDoppler()) { throw(AipsError("Empty or wrong MeasureHolder for asMDoppler")); } return dynamic_cast(*hold_p.ptr()); } const MEpoch &MeasureHolder::asMEpoch() const { if (!hold_p.ptr() || !isMEpoch()) { throw(AipsError("Empty or wrong MeasureHolder for asMEpoch")); } return dynamic_cast(*hold_p.ptr()); } const MFrequency &MeasureHolder::asMFrequency() const { if (!hold_p.ptr() || !isMFrequency()) { throw(AipsError("Empty or wrong MeasureHolder for asMFrequency")); } return dynamic_cast(*hold_p.ptr()); } const MPosition &MeasureHolder::asMPosition() const { if (!hold_p.ptr() || !isMPosition()) { throw(AipsError("Empty or wrong MeasureHolder for asMPosition")); } return dynamic_cast(*hold_p.ptr()); } const MRadialVelocity &MeasureHolder::asMRadialVelocity() const { if (!hold_p.ptr() || !isMRadialVelocity()) { throw(AipsError("Empty or wrong MeasureHolder for asMRadialVelocity")); } return dynamic_cast(*hold_p.ptr()); } const MEarthMagnetic &MeasureHolder::asMEarthMagnetic() const { if (!hold_p.ptr() || !isMEarthMagnetic()) { throw(AipsError("Empty or wrong MeasureHolder for asMEarthMagnetic")); } return dynamic_cast(*hold_p.ptr()); } const MBaseline &MeasureHolder::asMBaseline() const { if (!hold_p.ptr() || !isMBaseline()) { throw(AipsError("Empty or wrong MeasureHolder for asMBaseline")); } return dynamic_cast(*hold_p.ptr()); } const Muvw &MeasureHolder::asMuvw() const { if (!hold_p.ptr() || !isMuvw()) { throw(AipsError("Empty or wrong MeasureHolder for asMuvw")); } return dynamic_cast(*hold_p.ptr()); } 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.ptr()->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.ptr()->tellMe())) + " definition. DEFAULT (" + hold_p.ptr()->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.ptr()->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.ptr()->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.ptr()->putValue(vq)) { error += String("Illegal quantity in MeasureHolder value\n"); return False; } if (!setMV(i, *hold_p.ptr()->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.ptr() && putType(error, out)) { out.define(RecordFieldId("refer"), hold_p.ptr()->getRefString()); const Measure *off = hold_p.ptr()->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.ptr()->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.ptr() && 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.ptr()->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.clear(); if (tp == downcase(MDirection::showMe())) { hold_p.set(new MDirection()); } else if (tp == downcase(MDoppler::showMe())) { hold_p.set(new MDoppler()); } else if (tp == downcase(MEpoch::showMe())) { hold_p.set(new MEpoch()); } else if (tp == downcase(MFrequency::showMe())) { hold_p.set(new MFrequency()); } else if (tp == downcase(MPosition::showMe())) { hold_p.set(new MPosition()); } else if (tp == downcase(MRadialVelocity::showMe())) { hold_p.set(new MRadialVelocity()); } else if (tp == downcase(MBaseline::showMe())) { hold_p.set(new MBaseline()); } else if (tp == downcase(Muvw::showMe())) { hold_p.set(new Muvw()); } else if (tp == downcase(MEarthMagnetic::showMe())) { hold_p.set(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 PtrHolder 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-2.4.1/measures/Measures/MeasuresProxy.cc000066400000000000000000000601141321422335000220400ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError(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 (AipsError (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] *= C::pi/180/240./1.002737909350795; } } catch (AipsError(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.getRefPtr()->getType() != y.getRefPtr()->getType()) { y = MDirection::Convert(y, MDirection::castType (x.getRefPtr()->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.getRefPtr()->getType() != y.getRefPtr()->getType()) { y = MDirection::Convert(y, MDirection::castType (x.getRefPtr()->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-2.4.1/measures/Measures/MeasuresProxy.h000066400000000000000000000066461321422335000217140ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/measures/Measures/Muvw.cc000066400000000000000000000165551321422335000201620ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# Includes #include #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; } uInt Muvw::type() const { return Register(static_cast(0)); } void Muvw::assure(const Measure &in) { if (in.type() != Register(static_cast(0))) { 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 (in.type() != Register(static_cast(0))) 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()); } uInt Muvw::myType() { return Register(static_cast(0)); } 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-2.4.1/measures/Measures/Muvw.h000066400000000000000000000210631321422335000200120ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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; // Reference enum Types (included originally for gcc 2.95) typedef WHATEVER_SUN_TYPEDEF(Muvw) Types Types; //# 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(); virtual uInt type() const; 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 my type (as Register) static uInt myType(); // 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-2.4.1/measures/Measures/Nutation.cc000066400000000000000000000373351321422335000210240ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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); } CountedPtr > 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); } CountedPtr > 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); } CountedPtr > 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); } CountedPtr > 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); } CountedPtr > 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); } CountedPtr > 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); } CountedPtr > 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); } CountedPtr > 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-2.4.1/measures/Measures/Nutation.h000066400000000000000000000201561321422335000206570ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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-2.4.1/measures/Measures/ParAngleMachine.cc000066400000000000000000000167161321422335000222010ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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-2.4.1/measures/Measures/ParAngleMachine.h000066400000000000000000000161551321422335000220400ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/measures/Measures/Precession.cc000066400000000000000000000122521321422335000213240ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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-2.4.1/measures/Measures/Precession.h000066400000000000000000000165361321422335000211770ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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-2.4.1/measures/Measures/Quality.cc000066400000000000000000000047751321422335000206550ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/measures/Measures/SolarPos.cc000066400000000000000000000242371321422335000207620ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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); } CountedPtr > 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); } CountedPtr > 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-2.4.1/measures/Measures/SolarPos.h000066400000000000000000000144261321422335000206230ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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-2.4.1/measures/Measures/Stokes.cc000066400000000000000000000146361321422335000204720ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/measures/Measures/Stokes.h000066400000000000000000000124001321422335000203170ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/measures/Measures/UVWMachine.cc000066400000000000000000000223561321422335000211660ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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(-(C::pi_2 - in_p.getValue().get()(0)), 3, in_p.getValue().get()(1) - C::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(C::pi_2 - out_p.getValue().get()(1), 1, -(out_p.getValue().get()(0) - C::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(-(C::pi_2 - out_p.getValue().get()(1)), 1, out_p.getValue().get()(0) - in_p.getValue().get()(0), 3, (C::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-2.4.1/measures/Measures/UVWMachine.h000066400000000000000000000260771321422335000210340ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef MEASURES_UVWMACHINE_H #define MEASURES_UVWMACHINE_H //# Includes #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class MeasFrame; template class Vector; // 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-2.4.1/measures/Measures/VelocityMachine.cc000066400000000000000000000200161321422335000222720ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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 namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class MeasFrame; template class Vector; // 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-2.4.1/measures/Measures/test/000077500000000000000000000000001321422335000176605ustar00rootroot00000000000000casacore-2.4.1/measures/Measures/test/CMakeLists.txt000066400000000000000000000014141321422335000224200ustar00rootroot00000000000000set (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) 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-2.4.1/measures/Measures/test/SofaTest.cc000066400000000000000000000105261321422335000217230ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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-2.4.1/measures/Measures/test/dM1950_2000.cc000066400000000000000000000234311321422335000215120ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: dM1950_2000.cc,v 1.2 2007/08/30 22:56:31 wyoung Exp $ //# 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/C::_2pi; // 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 (AipsError x) { cout << x.getMesg() << endl; } return(0); } casacore-2.4.1/measures/Measures/test/dMeasure.cc000066400000000000000000000077371321422335000217520ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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 (AipsError x) { cout << x.getMesg() << endl; } return(0); } casacore-2.4.1/measures/Measures/test/dMeasure.in000066400000000000000000000000651321422335000217560ustar00rootroot00000000000000B1950 50 -60 12345 # Above 2 lines should be empty casacore-2.4.1/measures/Measures/test/dMeasure.out000066400000000000000000000006261321422335000221620ustar00rootroot00000000000000Demonstrate 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-2.4.1/measures/Measures/test/tEarthField.cc000066400000000000000000000115361321422335000223700ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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 (AipsError x) { cout << x.getMesg() << endl; } return 0; } casacore-2.4.1/measures/Measures/test/tEarthField.out000066400000000000000000000060271321422335000226110ustar00rootroot00000000000000Test 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-2.4.1/measures/Measures/test/tEarthMagneticMachine.cc000066400000000000000000000244631321422335000243640ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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 (AipsError x) { cout << x.getMesg() << endl; } return 0; } casacore-2.4.1/measures/Measures/test/tEarthMagneticMachine.out000066400000000000000000000052011321422335000245730ustar00rootroot00000000000000Test 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-2.4.1/measures/Measures/test/tIAU2000.cc000066400000000000000000000275041321422335000213030ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { cerr << x.getMesg() << 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-2.4.1/measures/Measures/test/tMBaseline.cc000066400000000000000000000203351321422335000222150ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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 << "type: " << x.type() << 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 << "myType: " << mb.myType() << endl; cout << "type: " << mb.type() << endl; cout << "All MBaseline functions: ok" << endl; cout << "---------------------------" << endl; delete mbc; } } catch (AipsError x) { cout << x.getMesg() << endl; } return 0; } casacore-2.4.1/measures/Measures/test/tMBaseline.out000066400000000000000000000134371321422335000224440ustar00rootroot00000000000000Test 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 type: 4 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 myType: 5 type: 5 All MBaseline functions: ok --------------------------- casacore-2.4.1/measures/Measures/test/tMDirection.cc000066400000000000000000000063371321422335000224210ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { cout << "tMDirection failed: " << x.getMesg() << endl; return 1; } return 0; } casacore-2.4.1/measures/Measures/test/tMEarthMagnetic.cc000066400000000000000000000242761321422335000232160ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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 << "type: " << x.type() << 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 << "myType: " << mb.myType() << endl; cout << "type: " << mb.type() << endl; cout << "All MEarthMagnetic functions: ok" << endl; cout << "---------------------------" << endl; } cout << "------------------------------------------" << endl; } catch (AipsError x) { cout << x.getMesg() << endl; } return 0; } casacore-2.4.1/measures/Measures/test/tMEarthMagnetic.out000066400000000000000000000141271321422335000234320ustar00rootroot00000000000000Test 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], [0.0023, 0.0023, 0.0023] earthMagneticAngle: -1.47878 earthMagneticAngle: -84.7277 deg get: [0.00398372, 0.785398, 0.61548] getRecordValue: [0.0023 nT, 0.0023 nT, 0.0023 nT] separation: 3.14159 separation: 180 deg crossProduct: [53.148, 38.4996, -91.6477] getAngle: [0.785398, 0.61548] rad getAngle: [45, 35.2644] deg getlength: 3.98372e-08 G radius: 0.00398372 putVector: [30, 40, 0], [30, 40, 0] VQ constructor: [0.0023, 0.0023, 0.0023] 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+: [30.0023, 40.0023, 0.0023] operator-: [29.9977, 39.9977, -0.0023] operator-pre-: [-0.0023, -0.0023, -0.0023] operator*: [0.0023, 0.00197536, 0.00258418] operator*: [0.0046, 0.0046, 0.0046] assure: ok getLength: 50 nT operator*: 2500 operator* 0.161 operator*: 2500 clone: [30, 40, 0] getVector: [30, 40, 0] near: 0 near: 0 nearAbs: 0 !=: 1 ==: 0 type: 3 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 myType: 4 type: 4 All MEarthMagnetic functions: ok --------------------------- ------------------------------------------ casacore-2.4.1/measures/Measures/test/tMFrequency.cc000066400000000000000000000034041321422335000224320ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: HostInfoDarwin.h 21521 2014-12-10 08:06:42Z gervandiepen $ #include #include #include int main() { try { AlwaysAssert( MFrequency::typeFromString("LSRK") == MFrequency::LSRK, AipsError ); Bool except = False; try { MFrequency::typeFromString("J2000"); } catch (const AipsError& x) { except = True; } AlwaysAssert(except, AipsError); cout << "ok" << endl; } catch (const AipsError& x) { cout << "tMFrequency failed: " << x.getMesg() << endl; return 1; } return 0; } casacore-2.4.1/measures/Measures/test/tMeasComet.cc000066400000000000000000000230451321422335000222340ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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 (AipsError x) { cout << x.getMesg() << endl; } try { cout << "--------------------------------------" << endl; cout << "Default VTOP" << endl; cout << "--------------------------------------" << endl; MeasComet comet; cout << "OK: " << comet.ok() << endl; cout << "Name: " << comet.getName() << endl; } catch (AipsError x) { cout << x.getMesg() << 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 (AipsError x) { cout << x.getMesg() << endl; } */ return 0; } casacore-2.4.1/measures/Measures/test/tMeasComet.out000066400000000000000000000253351321422335000224620ustar00rootroot00000000000000Test 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-2.4.1/measures/Measures/test/tMeasIERS.cc000066400000000000000000000057031321422335000217300ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: tMeasJPL.cc 21401 2013-11-28 11:39:17Z gervandiepen $ //# 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-2.4.1/measures/Measures/test/tMeasIERS.in_tgz000066400000000000000000173440211321422335000226440ustar00rootroot00000000000000‹ØÒ§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-2.4.1/measures/Measures/test/tMeasIERS.out000066400000000000000000000060611321422335000221500ustar00rootroot00000000000000Test 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-2.4.1/measures/Measures/test/tMeasIERS.run000077500000000000000000000006241321422335000221470ustar00rootroot00000000000000#!/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-2.4.1/measures/Measures/test/tMeasJPL.cc000066400000000000000000000175231321422335000216160ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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 AipsError& x) { cout << x.getMesg() << 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; } catch (const AipsError& x) { cout << x.getMesg() << endl; } return 0; } casacore-2.4.1/measures/Measures/test/tMeasJPL.out000066400000000000000000000310521321422335000220310ustar00rootroot00000000000000 *** 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 casacore-2.4.1/measures/Measures/test/tMeasJPL.run000077500000000000000000000003111321422335000220230ustar00rootroot00000000000000#!/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-2.4.1/measures/Measures/test/tMeasMath.cc000066400000000000000000000322661321422335000220630ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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 (AipsError x) { cout << x.getMesg() << endl; } try { cout << "Euler(10 deg, 20 m): "; cout << Euler(Quantity(10,"deg"), Quantity(20,"m")) << endl; } catch (AipsError x) { cout << x.getMesg() << 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 (AipsError x) { cout << x.getMesg() << 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,"''") - (const Double) 0.2 * Quantity(nt1.derivativeEqox(45882.2)/C::arcsec,"''") << endl; cout << " equation at 45882.5 from derivative at 45882.54: " << nt1.getEqoxAngle(45882.04,"''") - (const Double) 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 (AipsError x) { cout << x.getMesg() << 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 (AipsError x) { cout << x.getMesg() << 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 (AipsError x) { cout << x.getMesg() << endl; } return(0); } casacore-2.4.1/measures/Measures/test/tMeasMath.out000066400000000000000000000453631321422335000223070ustar00rootroot00000000000000Test 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-2.4.1/measures/Measures/test/tMeasure.cc000066400000000000000000000666561321422335000217770ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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; { 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 (AipsError x) { cout << x.getMesg() << 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 (AipsError x) { cout << x.getMesg() << 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 (AipsError x) { cout << x.getMesg() << 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 (AipsError x) { cout << x.getMesg() << endl; } return 0; } casacore-2.4.1/measures/Measures/test/tMeasureHolder.out000066400000000000000000000044271321422335000233430ustar00rootroot00000000000000---------------------------------------------------- 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-2.4.1/measures/Measures/test/tMuvw.cc000066400000000000000000000225311321422335000213140ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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 << "type: " << x.type() << 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 << "myType: " << mb.myType() << endl; cout << "Original: " << mb << endl; cout << "type: " << mb.type() << 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 (AipsError x) { cout << x.getMesg() << endl; } try { cout << "Test Muvw exception" << endl; cout << "---------------------------" << endl; MEpoch x; Muvw::assure(x); } catch (AipsError x) { cout << x.getMesg() << endl; } try { cout << "Test MVuvw exception" << endl; cout << "---------------------------" << endl; MVEpoch x; MVuvw::assure(x); } catch (AipsError x) { cout << x.getMesg() << endl; } cout << "---------------------------" << endl; return 0; } casacore-2.4.1/measures/Measures/test/tMuvw.out000066400000000000000000000145041321422335000215370ustar00rootroot00000000000000Test 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 type: 4 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 myType: 5 Original: uvw: [100, 10, 0] type: 5 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-2.4.1/measures/Measures/test/tNutation.cc000066400000000000000000000043131321422335000221550ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: Nutation.cc 21420 2014-03-19 09:18:51Z gervandiepen $ //# 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. #ifdef _OPENMP #pragma omp parallel for #endif 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-2.4.1/measures/Measures/test/tQuality.cc000066400000000000000000000100561321422335000220050ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# Includes #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-2.4.1/measures/Measures/test/tQuality.out000066400000000000000000000005731321422335000222320ustar00rootroot00000000000000int_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-2.4.1/measures/Measures/test/tRecordTransformable.cc000066400000000000000000000055561321422335000243240ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { cerr << x.getMesg() << endl; cout << "FAIL" << endl; return 1; } cout << "OK" << endl; return 0; } casacore-2.4.1/measures/Measures/test/tStokes.cc000066400000000000000000000106321321422335000216250ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# Includes #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-2.4.1/measures/Measures/test/tUVWMachine.cc000066400000000000000000000277651321422335000223420ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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 (AipsError x) { cout << x.getMesg() << 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 (AipsError x) { cout << x.getMesg() << endl; } return 0; } casacore-2.4.1/measures/Measures/test/tUVWMachine.out000066400000000000000000000127331321422335000225510ustar00rootroot00000000000000Test 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-2.4.1/measures/Measures/test/tVelocityMachine.cc000066400000000000000000000141261321422335000234420ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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 AipsError& x) {} MVFrequency restfrq3(-1); VelocityMachine bogus2( frqref, Unit("GHz"), restfrq3, velref, Unit("km/s") ); try { bogus2.makeVelocity(20); AlwaysAssert(False, AipsError); } catch (const AipsError& x) {} } } catch (AipsError x) { cout << x.getMesg() << endl; } return 0; } casacore-2.4.1/measures/Measures/test/tVelocityMachine.out000066400000000000000000000024471321422335000236670ustar00rootroot00000000000000Test 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-2.4.1/measures/TableMeasures.h000066400000000000000000000143361321422335000200310ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/measures/TableMeasures/000077500000000000000000000000001321422335000176515ustar00rootroot00000000000000casacore-2.4.1/measures/TableMeasures/ArrayMeasColumn.h000066400000000000000000000246451321422335000230770ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef MEASURES_ARRAYMEASCOLUMN_H #define MEASURES_ARRAYMEASCOLUMN_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations template class ArrayColumn; template class ScalarColumn; template class Array; template class ScalarMeasColumn; // // Read only access to 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 (uInt rownr, Array& meas, Bool resize = False) const; Array operator() (uInt 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 (uInt 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 (uInt rownr, const MeasRef& measRef) const; Array convert (uInt 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 (uInt 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 (uInt 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-2.4.1/measures/TableMeasures/ArrayMeasColumn.tcc000066400000000000000000000404471321422335000234170ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (uInt 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() (uInt rownr) const { Array meas; get(rownr, meas); return meas; } template Array ArrayMeasColumn::convert (uInt rownr, const MeasRef& measRef) const { typename M::Convert conv; conv.setOut (measRef); return doConvert (rownr, conv); } template Array ArrayMeasColumn::convert (uInt rownr, uInt refCode) const { typename M::Convert conv; conv.setOut (typename M::Types(refCode)); return doConvert (rownr, conv); } template Array ArrayMeasColumn::doConvert (uInt 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 (uInt 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-2.4.1/measures/TableMeasures/ArrayQuantColumn.h000066400000000000000000000236271321422335000233010ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (uInt 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 (uInt 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 (uInt 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 (uInt rownr, Array >& q, const Quantum& other, Bool resize = False) const; // // Return the quantum array stored in the specified row. // Array > operator() (uInt rownr) const; // Return the quantum array stored in the specified row, converted // to the given unit. Array > operator() (uInt rownr, const Unit&) const; // Return the quantum array stored in the specified row, converted // to the given units. Array > operator() (uInt rownr, const Vector&) const; // Return the quantum array stored in the specified row, converted // to the unit in other. Array > operator() (uInt 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 (uInt 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 (uInt 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-2.4.1/measures/TableMeasures/ArrayQuantColumn.tcc000066400000000000000000000312501321422335000236120ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (uInt 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 (uInt rownr, Array >& q, Bool resize) const { if (itsConvOut) { get (rownr, q, itsUnitOut, resize); } else { getData (rownr, q, resize); } } template void ArrayQuantColumn::get (uInt 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 (uInt 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 (uInt rownr, Array >& q, const Quantum& other, Bool resize) const { get (rownr, q, other.getFullUnit(), resize); } template Array > ArrayQuantColumn::operator() (uInt rownr) const { Array > q; get (rownr, q); return q; } template Array > ArrayQuantColumn::operator() (uInt rownr, const Unit& u) const { Array > q; get (rownr, q, u); return q; } template Array > ArrayQuantColumn::operator() (uInt rownr, const Vector& u) const { Array > q; get (rownr, q, u); return q; } template Array > ArrayQuantColumn::operator() (uInt 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 (uInt 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); } //# Declare extern templates for often used types. #ifdef AIPS_CXX11 extern template class Array >; #endif } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/measures/TableMeasures/ScalarMeasColumn.h000066400000000000000000000231751321422335000232230ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (uInt 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 (uInt rownr, M& meas) const; M operator() (uInt 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 (uInt 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 (uInt rownr, const MeasRef& measRef) const; M convert (uInt 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 (uInt rownr, const M& meas); // protected: // Make a MeasRef for the given row. MeasRef makeMeasRef (uInt 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-2.4.1/measures/TableMeasures/ScalarMeasColumn.tcc000066400000000000000000000273621321422335000235470ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (uInt 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 (uInt rownr, const MeasRef& measRef) const { M tmp; get (rownr, tmp); return typename M::Convert(tmp, measRef)(); } template M ScalarMeasColumn::convert (uInt rownr, uInt refCode) const { M tmp; get (rownr, tmp); return typename M::Convert(tmp, typename M::Types(refCode))(); } template M ScalarMeasColumn::operator() (uInt rownr) const { M meas; get (rownr, meas); return meas; } template MeasRef ScalarMeasColumn::makeMeasRef (uInt 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 (uInt 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-2.4.1/measures/TableMeasures/ScalarQuantColumn.h000066400000000000000000000220211321422335000234130ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (uInt rownr, Quantum& q) const; // Get the quantum in the specified row, converted to the given unit. void get (uInt rownr, Quantum& q, const Unit&) const; // Get the quantum in the specified row, converted to the unit in other. void get (uInt rownr, Quantum& q, const Quantum& other) const; // // Return the quantum stored in the specified row. // Quantum operator() (uInt rownr) const; // Return the quantum stored in the specified row, converted to the // given unit. Quantum operator() (uInt rownr, const Unit&) const; // Return the quantum in the specified row, converted to the unit in // other. Quantum operator() (uInt 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 (uInt 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. 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 (uInt 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-2.4.1/measures/TableMeasures/ScalarQuantColumn.tcc000066400000000000000000000164761321422335000237560ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (uInt 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 (uInt rownr, Quantum& q) const { getData (rownr, q); if (itsConvOut) { q.convert (itsUnitOut); } } template void ScalarQuantColumn::get (uInt rownr, Quantum& q, const Unit& u) const { getData (rownr, q); q.convert (u); } template void ScalarQuantColumn::get (uInt rownr, Quantum& q, const Quantum& other) const { getData (rownr, q); q.convert (other); } template Quantum ScalarQuantColumn::operator() (uInt rownr) const { Quantum q; get (rownr, q); return q; } template Quantum ScalarQuantColumn::operator() (uInt rownr, const Unit& u) const { Quantum q; get (rownr, q, u); return q; } template Quantum ScalarQuantColumn::operator() (uInt rownr, const Quantum& other) const { Quantum q; get (rownr, q, other); return q; } template void ScalarQuantColumn::put (uInt 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 SHARED_PTR > > ScalarQuantColumn::getColumn(const Unit& unit) const { 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-2.4.1/measures/TableMeasures/TableMeasColumn.cc000066400000000000000000000065761321422335000232110ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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 = 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 (uInt 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-2.4.1/measures/TableMeasures/TableMeasColumn.h000066400000000000000000000136001321422335000230350ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (uInt 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.null(); } // 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. CountedPtr 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-2.4.1/measures/TableMeasures/TableMeasDesc.h000066400000000000000000000266421321422335000224700ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/measures/TableMeasures/TableMeasDesc.tcc000066400000000000000000000070221321422335000230010ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/measures/TableMeasures/TableMeasDescBase.cc000066400000000000000000000157471321422335000234250ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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-2.4.1/measures/TableMeasures/TableMeasOffsetDesc.cc000066400000000000000000000120551321422335000237660ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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-2.4.1/measures/TableMeasures/TableMeasOffsetDesc.h000066400000000000000000000166621321422335000236400ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/measures/TableMeasures/TableMeasRefDesc.cc000066400000000000000000000235351321422335000232610ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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-2.4.1/measures/TableMeasures/TableMeasRefDesc.h000066400000000000000000000302571321422335000231220ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/measures/TableMeasures/TableMeasType.cc000066400000000000000000000064471321422335000226720ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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-2.4.1/measures/TableMeasures/TableMeasType.h000066400000000000000000000074331321422335000225300ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/measures/TableMeasures/TableMeasValueDesc.cc000066400000000000000000000061621321422335000236160ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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-2.4.1/measures/TableMeasures/TableMeasValueDesc.h000066400000000000000000000122461321422335000234600ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/measures/TableMeasures/TableMeas_tmpl.cc000066400000000000000000000027531321422335000230600ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: Array.h 21545 2015-01-22 19:36:35Z gervandiepen $ //# Includes #include //# Instantiate extern templates for often used types. #ifdef AIPS_CXX11 namespace casacore { template class Array >; } #endif casacore-2.4.1/measures/TableMeasures/TableQuantumDesc.cc000066400000000000000000000126441321422335000233700ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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 { throw(AipsError("TableQuantum::reconstruct; Not a Quantum" " column: " + columnName)); } } 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-2.4.1/measures/TableMeasures/TableQuantumDesc.h000066400000000000000000000331741321422335000232330ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/measures/TableMeasures/test/000077500000000000000000000000001321422335000206305ustar00rootroot00000000000000casacore-2.4.1/measures/TableMeasures/test/CMakeLists.txt000066400000000000000000000004511321422335000233700ustar00rootroot00000000000000set (tests tTableQuantum tTableMeasures dVarRefMdirCol ) foreach (test ${tests}) add_executable (${test} ${test}.cc) target_link_libraries (${test} casa_measures) add_test (${test} ${CMAKE_SOURCE_DIR}/cmake/cmake_assay ./${test}) add_dependencies(check ${test}) endforeach (test) casacore-2.4.1/measures/TableMeasures/test/dVarRefMdirCol.cc000066400000000000000000000124161321422335000237460ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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 (AipsError& x) { cout << "An error occurred. The test ended early with the following"; cout << " message:\n"; cout << x.getMesg() << endl; return 1; } return 0; } casacore-2.4.1/measures/TableMeasures/test/tTableMeasures.cc000066400000000000000000001526001321422335000240630ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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 (AipsError x) { cout << "The following should report that the column's "; cout << "unit vector is too long.\n"; cout << x.getMesg() << endl; } try { // test TableMeasValueDesc - invalid unit Vector u(1); u(0) = "m"; TableMeasValueDesc tCol(td, "Time2Arr"); TableMeasDesc tmp(tCol, u); } catch (AipsError x) { cout << "The following should report that the column "; cout << "has an invalid unit.\n"; cout << x.getMesg() << 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 (AipsError x) { cout << "Attempt to reference undefined Measure offset "; cout << " exception on the TableMeasOffsetDesc object.\n"; cout << x.getMesg() << 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 (AipsError x) { cout << "The following line should report an error "; cout << "in ScalarMeasColumn::setDescUnits - invalid unit.\n"; cout << x.getMesg() << 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 (AipsError 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.getMesg() << 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 (AipsError 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.getMesg() << 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 (AipsError x) { cout << "The following line should be a "; cout << "null column exception.\n"; cout << x.getMesg() << 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 (AipsError) { excp = True; } AlwaysAssertExit (excp); excp = False; try { arrayCol.setDescOffset (obsTime); } catch (AipsError) { 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 (AipsError x) { cout << "The following line should be a "; cout << "Table array conformance error exception.\n"; cout << x.getMesg() << 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 (AipsError x) { cout << "An error occurred. The test ended early with the following"; cout << " message:\n"; cout << x.getMesg() << endl; return 1; } return 0; } casacore-2.4.1/measures/TableMeasures/test/tTableMeasures.out000066400000000000000000000252411321422335000243050ustar00rootroot00000000000000Test 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-2.4.1/measures/TableMeasures/test/tTableQuantum.cc000066400000000000000000000512141321422335000237300ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { cout << "A no such column message should follow\n"; cout << x.getMesg() << endl; } try { // variable unit's column doesn't exist. TableQuantumDesc taexcep(td, "ScaQuantComplex", "SillyName"); } catch (AipsError x) { cout << "A no such unit's column message should follow\n"; cout << x.getMesg() << 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 (AipsError x) { cout << "A message about an incorrect variable unit's type...\n"; cout << x.getMesg() << 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 (AipsError x) { cout << "Catch an AipsError. Column is null...\n"; cout << x.getMesg() << 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 (AipsError x) { cout << "Catch an AipsError. Array column is null...\n"; cout << x.getMesg() << endl; } // test attaching a bogus quantum column try { // create with a real column but not a quantum column ArrayQuantColumn testCol(qtab, "BogusQuantCol"); } catch (AipsError x) { cout << "Exception should report not a quantum column...\n"; cout << x.getMesg() << 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 (AipsError x) { cout << "The following line should be a "; cout << "Table array conformance error exception.\n"; cout << x.getMesg() << 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"); SHARED_PTR > > v = col.getColumn(); AlwaysAssert(v->getValue().size() == 5, AipsError); SHARED_PTR > > w = col.getColumn("rad"); AlwaysAssert(w->getValue().size() == 5, AipsError); Double frac = C::pi/180; for (uInt i=0; i<5; ++i) { AlwaysAssert( near(w->getValue()[i], frac*v->getValue()[i]), AipsError ); } ScalarQuantColumn ccol(qtab, "ScaQuantComplex"); SHARED_PTR > > x = ccol.getColumn(); AlwaysAssert(x->getValue().size() == 5, AipsError); SHARED_PTR > > y = ccol.getColumn("rad"); for (uInt i=0; i<5; ++i) { AlwaysAssert( near(y->getValue()[i], frac*x->getValue()[i]), AipsError ); } } } catch (AipsError x) { cout << "Unexpected exception1: " << x.getMesg() << 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-2.4.1/measures/apps/000077500000000000000000000000001321422335000160605ustar00rootroot00000000000000casacore-2.4.1/measures/apps/CMakeLists.txt000066400000000000000000000007201321422335000206170ustar00rootroot00000000000000foreach(prog findmeastable) add_executable (${prog} ${prog}.cc) target_link_libraries (${prog} casa_measures ${CASACORE_ARCH_LIBS}) install(TARGETS ${prog} DESTINATION bin) endforeach(prog findmeastable) add_executable (measuresdata measuresdata/measuresdata.cc) target_link_libraries (measuresdata casa_measures ${CASACORE_ARCH_LIBS}) install(TARGETS measuresdata DESTINATION bin) install(PROGRAMS measuresdata/measuresdata-update DESTINATION bin) casacore-2.4.1/measures/apps/findmeastable.cc000066400000000000000000000052401321422335000211660ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/measures/apps/measuresdata/000077500000000000000000000000001321422335000205365ustar00rootroot00000000000000casacore-2.4.1/measures/apps/measuresdata/measuresdata-update000077500000000000000000000034051321422335000244240ustar00rootroot00000000000000#!/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-) 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 "$ftp" ] ; then url="ftp://$ftp/$dir/$file" else echo Severe: missing ftp/http 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-2.4.1/measures/apps/measuresdata/measuresdata.cc000066400000000000000000001652711321422335000235370ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: measuresdata.cc,v 1.12 2008/11/01 19:11:28 wbrouw Exp $ //# 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 IGRF"}, { "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(), { "maia.usno.navy.mil", "ser7", "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", "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", "eopc04_IAU2000.xx" } }, //**********************************************************************// { "IERSpredict", 2.0, 3.0, False, 0.0, 1.0, "geodetic/IERSpredict", "ftp", "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", "ftp", "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", "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; } // 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 (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 ROScalarColumn(*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 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; // Check if header present Path hpath(tprop.vinfo[0]); if (hpath.isValid() && File(hpath).exists() && File(hpath).isReadable()) { tprop.fileAddress[2] = tprop.vinfo[1]; tprop.fileAddress[2].replace(4, 4, uIntToString(1960)); //// } else { tprop.fileAddress[2] = tprop.vinfo[0]; return True; // Get header first }; // Dates Int stdat = Int(MVTime(1960, 1, 1).day()); // Check if in present and to be used if (inVal.testOnly || !inVal.x__fn || tprop.fileAddress[2] != inVal.in) 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); Double endepo(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; Double st1; 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) tprop.MJD0 = ((stdat-stepo)/incepo-1)*incepo + stepo; 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 tab->addRow(allmjd.size()); for (uInt i=0; iputScalar(i, allmjd[i]); Vector colA(allcol[i]); static_cast*>(tprop.columns[1])->put(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-2.4.1/mirlib/000077500000000000000000000000001321422335000145475ustar00rootroot00000000000000casacore-2.4.1/mirlib/CMakeLists.txt000066400000000000000000000010211321422335000173010ustar00rootroot00000000000000# # 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-2.4.1/mirlib/README000066400000000000000000000043471321422335000154370ustar00rootroot00000000000000# 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: aips2-request@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-2.4.1/mirlib/bug.c000066400000000000000000000325221321422335000154740ustar00rootroot00000000000000/************************************************************************/ /* */ /* 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 */ /************************************************************************/ #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-2.4.1/mirlib/dio.c000066400000000000000000000246771321422335000155060ustar00rootroot00000000000000/************************************************************************/ /* 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_IFDIR & buf.st_mode)Strcat(path,"/"); } } casacore-2.4.1/mirlib/headio.c000066400000000000000000000647761321422335000161700ustar00rootroot00000000000000/************************************************************************/ /* */ /* 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-2.4.1/mirlib/hio.c000066400000000000000000001331661321422335000155040ustar00rootroot00000000000000/* 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-2.4.1/mirlib/hio.h000066400000000000000000000020001321422335000154670ustar00rootroot00000000000000#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-2.4.1/mirlib/io.h000066400000000000000000000034411321422335000153310ustar00rootroot00000000000000/************************************************************************/ /* */ /* 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-2.4.1/mirlib/key.c000066400000000000000000000755431321422335000155210ustar00rootroot00000000000000/*********************************************************************** * * 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-2.4.1/mirlib/maskio.c000066400000000000000000000333631321422335000162060ustar00rootroot00000000000000/************************************************************************/ /* */ /* 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-2.4.1/mirlib/maxdimc.h000066400000000000000000000020311321422335000163360ustar00rootroot00000000000000/* ------------------------------------------------------------- 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-2.4.1/mirlib/miriad.h000066400000000000000000000417741321422335000162020ustar00rootroot00000000000000/* // 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-2.4.1/mirlib/pack.c000066400000000000000000000442101321422335000156320ustar00rootroot00000000000000/* 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-2.4.1/mirlib/scrio.c000066400000000000000000000137251321422335000160420ustar00rootroot00000000000000/************************************************************************/ /* */ /* 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-2.4.1/mirlib/uvio.c000066400000000000000000005072261321422335000157110ustar00rootroot00000000000000/************************************************************************/ /* 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) ) /*----------------------------------------------------------------------*/ /* */ /* 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 (1) { 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; } #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-2.4.1/mirlib/xyio.c000066400000000000000000000413301321422335000157040ustar00rootroot00000000000000/************************************************************************/ /* */ /* 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-2.4.1/mirlib/xyzio.c000066400000000000000000002462621321422335000161110ustar00rootroot00000000000000/******************************************************************************* 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-2.4.1/ms/000077500000000000000000000000001321422335000137105ustar00rootroot00000000000000casacore-2.4.1/ms/CMakeLists.txt000066400000000000000000000177221321422335000164610ustar00rootroot00000000000000# # CASA MeasurementSets # set ( parser_inputs MSAntennaGram MSArrayGram MSCorrGram MSFeedGram MSFieldGram MSObservationGram MSScanGram MSSpwGram MSTimeGram MSUvDistGram MSStateGram ) foreach (src ${parser_inputs}) BISON_TARGET (${src} MSSel/${src}.yy ${CMAKE_CURRENT_BINARY_DIR}/${src}.ycc COMPILE_FLAGS "-y -p ${src} --output-file=${src}.ycc") 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} ) 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 MeasurementSets.h MSSel.h MSOper.h DESTINATION include/casacore/ms ) add_subdirectory (MeasurementSets/test ${EXCL_ALL}) add_subdirectory (MSSel/test ${EXCL_ALL}) add_subdirectory (MSOper/test ${EXCL_ALL}) casacore-2.4.1/ms/MSOper.h000066400000000000000000000036011321422335000152260ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: MeasurementSets.h 21538 2015-01-07 09:08:57Z gervandiepen $ #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-2.4.1/ms/MSOper/000077500000000000000000000000001321422335000150555ustar00rootroot00000000000000casacore-2.4.1/ms/MSOper/MS1ToMS2Converter.cc000066400000000000000000000752621321422335000205150ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include #include #include #include #include #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; { ROArrayColumn delDir(fldTab,"DELAY_DIR"); ROArrayColumn delDirRate(fldTab,"DELAY_DIR_RATE"); ROArrayColumn phaseDir(fldTab,"PHASE_DIR"); ROArrayColumn phaseDirRate(fldTab,"PHASE_DIR_RATE"); ROArrayColumn pointingDir(fldTab,"POINTING_DIR"); ROArrayColumn pointingDirRate(fldTab,"POINTING_DIR_RATE"); ROArrayColumn refDir(fldTab,"REFERENCE_DIR"); ROArrayColumn 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); ROScalarColumn arrName(arrTab,"NAME"); ScalarColumn telName(obsTab,"TELESCOPE_NAME"); ArrayColumn timeRange(obsTab, "TIME_RANGE"); ScalarColumn flagRow(obsTab,"FLAG_ROW"); flagRow.fillColumn(False); ROScalarColumn time(t, "TIME"); ROScalarColumn interval(t, "INTERVAL"); ROScalarColumn observationid(t, "OBSERVATION_ID"); ROScalarColumn 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); ROScalarColumn time(t, MS::columnName(MS::TIME)); ROScalarColumn 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; { ROArrayColumn 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; ROScalarColumn 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"); ROArrayColumn scorrType(spwTab, "CORR_TYPE"); ROArrayColumn 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); ROScalarColumn 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)); ROArrayColumn sigma (t, "SIGMA"); ROScalarColumn 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-2.4.1/ms/MSOper/MS1ToMS2Converter.h000066400000000000000000000067061321422335000203540ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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(); // Do the actual conversion. Bool convert(); private: // Forbid copy constrcutor and assignment. // MS1ToMS2Converter (const MS1ToMS2Converter&); MS1ToMS2Converter& operator= (const MS1ToMS2Converter&); // // 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-2.4.1/ms/MSOper/MSConcat.cc000066400000000000000000003062711321422335000170440ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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())), newSourceIndex_p(-1), newSourceIndex2_p(-1), newSPWIndex_p(-1), newObsIndexA_p(-1), newObsIndexB_p(-1), otherObsIdsWithCounterpart_p(-1), solSystObjects_p(-1) { itsDirTol=Quantum(1.0, "mas"); itsFreqTol=Quantum(1.0, "Hz"); itsWeightScale = 1.; itsRespectForFieldName = False; doSource_p=False; doObsA_p = doObsB_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 ROMSFieldColumns& 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(uInt i=0; i 0) { if (itsFixedShape.nelements() > 0) { const ROMSPolarizationColumns otherPolCols(otherMS.polarization()); const ROMSSpWindowColumns otherSpwCols(otherMS.spectralWindow()); const ROMSDataDescColumns otherDDCols(otherMS.dataDescription()); const uInt nShapes = otherDDCols.nrow(); for (uInt s = 0; s < nShapes; s++) { checkShape(getShape(otherDDCols, otherSpwCols, otherPolCols, s)); } } const ROMSMainColumns 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 Vector delrows(itsMS.state().nrow()); indgen(delrows); itsMS.state().removeRow(delrows); } else{ // both state tables are filled const uInt oldStateRows = itsMS.state().nrow(); newStateIndices = copyState(otherMS.state()); const uInt addedRows = itsMS.state().nrow() - oldStateRows; const uInt 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 { uInt oldSRows = itsMS.source().nrow(); copySource(otherMS); if(Table::isReadable(itsMS.sourceTableName())){ uInt addedRows = itsMS.source().nrow() - oldSRows; if(addedRows>0){ log << "Added " << addedRows << " rows to the source subtable" << LogIO::POST; } } } // DATA_DESCRIPTION uInt oldRows = itsMS.dataDescription().nrow(); uInt oldSPWRows = itsMS.spectralWindow().nrow(); const Block newDDIndices = copySpwAndPol(otherMS.spectralWindow(), otherMS.polarization(), otherMS.dataDescription()); { uInt addedRows = itsMS.dataDescription().nrow() - oldRows; uInt 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())){ uInt 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(); uInt oldFeedRows = itsMS.feed().nrow(); const Block newAntIndices = copyAntennaAndFeed(otherMS.antenna(), otherMS.feed()); Bool antIndexTrivial = True; for(uInt ii=0; ii newFldIndices = copyField(otherMS); { const uInt addedRows = itsMS.field().nrow() - oldRows; const uInt 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); // POINTING if(!antIndexTrivial){ copyPointingB(otherMS.pointing(), newAntIndices); } ///////////////////////////////////////////////////// // 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 uInt otherRows = otherMS.nrow(); const uInt 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(); 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& thisScanCol = mainCols.scanNumber(); ScalarColumn& thisStateIdCol = mainCols.stateId(); ScalarColumn& thisObsIdCol = mainCols.observationId(); 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; if (doState && !otherStateNull){ otherStateId = otherStateIdCol.getColumn(); } Int defaultScanOffset=0; SimpleOrderedMap scanOffsetForOid(-1); SimpleOrderedMap encountered(-1); vector distinctObsIdSet; vector minScan; vector maxScan; if(reindexObsidAndScan){ otherObsIds = otherObsIdCol.getColumn(); Vector theseObsIds=thisObsIdCol.getColumn(); Vector theseScans=thisScanCol.getColumn(); if(doObsA_p){ // the obs ids changed for the first table for(uInt r = 0; r < theseRows; r++) { if(newObsIndexA_p.isDefined(theseObsIds[r])){ // apply change theseObsIds[r] = newObsIndexA_p(theseObsIds[r]); } } thisObsIdCol.putColumn(theseObsIds); } // 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(uInt 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); // MAIN Bool doWeightScale = (itsWeightScale!=1.) && (itsWeightScale!=0.); Float sScale = 1.; // scale for SIGMA if (doWeightScale){ sScale = 1/sqrt(itsWeightScale); } if(reindexObsidAndScan){ for (uInt r = 0; r < otherRows; r++) { Int oid = 0; if(doObsB_p && newObsIndexB_p.isDefined(otherObsIds[r])){ // the obs ids have been changed for the table to be appended oid = newObsIndexB_p(otherObsIds[r]); } else{ // this OBS id didn't change oid = otherObsIds[r]; } if(oid != otherObsIds[r]){ // obsid actually changed if(!scanOffsetForOid.isDefined(oid)){ // offset not set, use default scanOffsetForOid.define(oid, defaultScanOffset); } if(!encountered.isDefined(oid) && scanOffsetForOid(oid)!=0){ log << LogIO::NORMAL << "Will offset scan numbers by " << scanOffsetForOid(oid) << " for observations with Obs ID " << oid << " in order to make scan numbers unique." << LogIO::POST; encountered.define(oid,0); } otherScan[r] = otherScan[r] + scanOffsetForOid(oid); } otherObsIds[r] = oid; } // update or create the file with the initial values for the next concat Int maxScanOther = 0; for(uInt 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; inewA2){ // 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; 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){ otherData.put(r, conj(reversedData)); } else{ otherData.put(r, reversedData); } } if(doCorrectedData){ if(doConjugateVis){ otherCorrectedData.put(r, conj(reversedCorrData)); } else{ otherCorrectedData.put(r, reversedCorrData); } } if(doModelData){ if(doConjugateVis){ otherModelData.put(r, conj(reversedModData)); } else{ otherModelData.put(r, reversedModData); } } } else{ // no reversal if(!doFloatData){ if(doConjugateVis){ // conjugate because order of antennas was reversed otherData.put(r, conj(otherData(r))); } } if(doModelData){ if(doConjugateVis){ otherModelData.put(r, conj(otherModelData(r))); } } if(doCorrectedData){ if(doConjugateVis){ otherCorrectedData.put(r, conj(otherCorrectedData(r))); } } } // end if itsChanReversed otherDDId[r] = newDDIndices[otherDDId[r]]; otherFieldId[r] = newFldIndices[otherFieldId[r]]; if(doWeightScale){ 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); } } // 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); } 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 ROMSFieldColumns 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 ROMSMainColumns otherMainCols(otherMS); if (otherMS.nrow() > 0) { if (itsFixedShape.nelements() > 0) { const ROMSPolarizationColumns otherPolCols(otherMS.polarization()); const ROMSSpWindowColumns otherSpwCols(otherMS.spectralWindow()); const ROMSDataDescColumns otherDDCols(otherMS.dataDescription()); const uInt nShapes = otherDDCols.nrow(); for (uInt 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 Vector delrows(itsMS.state().nrow()); indgen(delrows); itsMS.state().removeRow(delrows); } else{ // both state tables are filled const uInt oldStateRows = itsMS.state().nrow(); newStateIndices = copyState(otherMS.state()); const uInt addedRows = itsMS.state().nrow() - oldStateRows; const uInt 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 { uInt oldSRows = itsMS.source().nrow(); copySource(otherMS); if(Table::isReadable(itsMS.sourceTableName())){ uInt addedRows = itsMS.source().nrow() - oldSRows; if(addedRows>0){ log << "Added " << addedRows << " rows to the source subtable" << LogIO::POST; } } } // DATA_DESCRIPTION uInt oldRows = itsMS.dataDescription().nrow(); uInt oldSPWRows = itsMS.spectralWindow().nrow(); const Block newDDIndices = copySpwAndPol(otherMS.spectralWindow(), otherMS.polarization(), otherMS.dataDescription()); { uInt addedRows = itsMS.dataDescription().nrow() - oldRows; uInt 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())){ uInt 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(); uInt oldFeedRows = itsMS.feed().nrow(); const Block newAntIndices = copyAntennaAndFeed(otherMS.antenna(), otherMS.feed()); { uInt addedRows = itsMS.antenna().nrow() - oldRows; uInt 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 uInt addedRows = itsMS.field().nrow() - oldRows; const uInt 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); // 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 ; Vector delrows(itsMS.pointing().nrow()); indgen(delrows); itsMS.pointing().removeRow(delrows); } ////////////////////////////////////////////////////// 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 uInt newRows = otherMS.nrow(); uInt 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 ROScalarColumn& otherTime = otherMainCols.time(); ScalarColumn& thisTime = destMainCols.time(); const ROScalarColumn& otherInterval = otherMainCols.interval(); ScalarColumn& thisInterval = destMainCols.interval(); const ROScalarColumn& otherExposure = otherMainCols.exposure(); ScalarColumn& thisExposure = destMainCols.exposure(); const ROScalarColumn& otherTimeCen = otherMainCols.timeCentroid(); ScalarColumn& thisTimeCen = destMainCols.timeCentroid(); const ROScalarColumn& otherArrayId = otherMainCols.arrayId(); ScalarColumn& thisArrayId = destMainCols.arrayId(); const ROArrayColumn& otherFlag = otherMainCols.flag(); ArrayColumn& thisFlag = destMainCols.flag(); const ROArrayColumn& otherFlagCat = otherMainCols.flagCategory(); ArrayColumn& thisFlagCat = destMainCols.flagCategory(); Bool copyFlagCat = !(thisFlagCat.isNull() || otherFlagCat.isNull()); copyFlagCat = copyFlagCat && thisFlagCat.isDefined(0) && otherFlagCat.isDefined(0); const ROScalarColumn& otherFlagRow = otherMainCols.flagRow(); ScalarColumn& thisFlagRow = destMainCols.flagRow(); const ROScalarColumn& otherFeed1 = otherMainCols.feed1(); ScalarColumn& thisFeed1 = destMainCols.feed1(); const ROScalarColumn& otherFeed2 = otherMainCols.feed2(); ScalarColumn& thisFeed2 = destMainCols.feed2(); // create column objects for those columns which potentially need to be modified ROArrayColumn otherData; ArrayColumn thisData; ROArrayColumn otherFloatData; ArrayColumn thisFloatData; ROArrayColumn 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 ROScalarColumn& otherAnt1 = otherMainCols.antenna1(); ScalarColumn thisAnt1 = destMainCols.antenna1(); const ROScalarColumn& otherAnt2 = otherMainCols.antenna2(); ScalarColumn thisAnt2 = destMainCols.antenna2(); const ROScalarColumn& otherDDId = otherMainCols.dataDescId(); ScalarColumn thisDDId = destMainCols.dataDescId(); const ROScalarColumn& otherFieldId = otherMainCols.fieldId(); ScalarColumn thisFieldId = destMainCols.fieldId(); const ROArrayColumn& otherUvw = otherMainCols.uvw(); ArrayColumn thisUvw = destMainCols.uvw(); const ROArrayColumn& otherWeight = otherMainCols.weight(); ArrayColumn thisWeight = destMainCols.weight(); const ROArrayColumn& otherWeightSp = otherMainCols.weightSpectrum(); ArrayColumn thisWeightSp = destMainCols.weightSpectrum(); const ROArrayColumn& otherSigma = otherMainCols.sigma(); ArrayColumn thisSigma = destMainCols.sigma(); const ROArrayColumn& otherSigmaSp = otherMainCols.sigmaSpectrum(); ArrayColumn thisSigmaSp = destMainCols.sigmaSpectrum(); const ROScalarColumn& otherScan = otherMainCols.scanNumber(); const ROScalarColumn& otherStateId = otherMainCols.stateId(); const ROScalarColumn& otherObsId=otherMainCols.observationId(); ScalarColumn thisScan; ScalarColumn thisStateId; ScalarColumn thisObsId; thisScan.reference(scanNumber()); thisStateId.reference(stateId()); thisObsId.reference(observationId()); Vector obsIds=otherObsId.getColumn(); if(doObsA_p){ // the obs ids changed for the first table Vector oldObsIds=thisObsId.getColumn(); for(uInt r = 0; r < curRow; r++) { if(newObsIndexA_p.isDefined(oldObsIds[r])){ // apply change thisObsId.put(r, newObsIndexA_p(oldObsIds[r])); } } } if(doState && otherStateNull){ // the state ids for the first table will have to be set to -1 for(uInt 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 SimpleOrderedMap scanOffsetForOid(-1); SimpleOrderedMap encountered(-1); vector distinctObsIdSet; vector minScan; vector maxScan; Int maxScanThis=0; for(uInt 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; { ROTableVector 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); } for (uInt r = 0; r < newRows; r++, curRow++) { 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.isDefined(obsIds[r])){ // the obs ids have been changed for the table to be appended oid = 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.isDefined(oid)){ // offset not set, use default scanOffsetForOid.define(oid, defaultScanOffset); } if(!encountered.isDefined(oid) && scanOffsetForOid(oid)!=0){ log << LogIO::NORMAL << "Will offset scan numbers by " << scanOffsetForOid(oid) << " for observations with Obs ID " << oid << " in order to make scan numbers unique." << LogIO::POST; encountered.define(oid,0); } thisScan.put(curRow, otherScan(r) + scanOffsetForOid(oid)); } else{ thisScan.put(curRow, otherScan(r)); } 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; 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){ thisData.put(curRow, conj(reversedData)); } else{ thisData.put(curRow, reversedData); } } if(doCorrectedData){ if(doConjugateVis){ thisCorrectedData.put(curRow, conj(reversedCorrData)); } else{ thisCorrectedData.put(curRow, reversedCorrData); } } if(doModelData){ if(doConjugateVis){ thisModelData.put(curRow, conj(reversedModData)); } else{ thisModelData.put(curRow, reversedModData); } } } else{ // no reversal if(doFloatData){ thisFloatData.put(curRow, otherFloatData, r); } else{ if(doConjugateVis){ // conjugate because order of antennas was reversed thisData.put(curRow, conj(otherData(r))); } else{ thisData.put(curRow, otherData, r); } } if(doModelData){ if(doConjugateVis){ thisModelData.put(curRow, conj(otherModelData(r))); } else{ thisModelData.put(curRow, otherModelData, r); } } if(doCorrectedData){ if(doConjugateVis){ thisCorrectedData.put(curRow, conj(otherCorrectedData(r))); } else{ thisCorrectedData.put(curRow, otherCorrectedData, r); } } } // end if itsChanReversed if(doWeightScale){ 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{ 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); 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 = 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 ROMSDataDescColumns& ddCols, const ROMSSpWindowColumns& spwCols, const ROMSPolarizationColumns& 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 ROMSMainColumns& 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; Vector delrows(itsMS.pointing().nrow()); indgen(delrows); itsMS.pointing().removeRow(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; Vector rowtodel(point.nrow()); indgen(rowtodel); point.removeRow(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; Vector delrows(otherPoint.nrow()); indgen(delrows); otherPoint.removeRow(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; // Vector 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; Vector delrows(itsMS.pointing().nrow()); indgen(delrows); itsMS.pointing().removeRow(delrows); Vector rowtodel(otherPoint.nrow()); indgen(rowtodel); otherPoint.removeRow(rowtodel); return False; } pointCol.antennaId().putColumn(antennaIDs); } 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(); SimpleOrderedMap tempObsIndex(-1); SimpleOrderedMap tempObsIndexReverse(-1); SimpleOrderedMap tempObsIndex2(-1); 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 (uInt k=0; k < otherObs.nrow() ; ++k){ obs.addRow(); ++actualRow; obsRow.put(actualRow, otherObsRow.get(k, True)); tempObsIndex.define(k, actualRow); tempObsIndexReverse.define(actualRow, k); } if(remRedunObsId){ // remove redundant rows MSObservationColumns& obsCol = observation(); Vector rowToBeRemoved(obs.nrow(), False); vector rowsToBeRemoved; for(uInt 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(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 ROMSAntennaColumns 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 ROMSFeedColumns 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; Vector 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(newSPWId) << endl; newSPWId = 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(); uInt 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(newSPWId) << endl; feedRecord.define(spwField, 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(); uInt 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(newSPWId) << endl; newSPWId = 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 ROMSStateColumns 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 uInt nFlds = otherFld.nrow(); Block fldMap(nFlds); const Quantum tolerance=itsDirTol; const ROMSFieldColumns 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(uInt 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 Vector sortedI(otherms.nrow()); ROMSMainColumns 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(AipsError 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(AipsError 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.isDefined(oldIndex)){ fieldCols.sourceId().put(fldMap[f], newSourceIndex_p(oldIndex)); } } if(doSource2_p){ Int oldIndex=fieldCols.sourceId()(fldMap[f]); if(newSourceIndex2_p.isDefined(oldIndex)){ fieldCols.sourceId().put(fldMap[f], newSourceIndex2_p(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(); ROMSSourceColumns 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.define(otherId(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 ROMSFieldColumns otherFieldCols(otherms.field()); const ROMSFieldColumns fieldCols(itsMS.field()); for(uInt 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 SimpleOrderedMap tempSourceIndex(-1); SimpleOrderedMap tempSourceIndex2(-1); SimpleOrderedMap tempSourceIndex3(-1); 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.isDefined(thisSPWId(j)+10000)){ // the SPW table was rearranged sourceCol.spectralWindowId().put(j, newSPWIndex_p(thisSPWId(j)+10000) ); //sourceRecord.define(sourceSPWId, newSPWIndex_p(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 = 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 = 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.define(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.define(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 = solSystObjects_p(thisId(j)); for (Int k=j+1 ; k < newNumrows_this ; ++k){ if(thisSourceId(j)!=thisSourceId(k)){ Int reftypek = 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.define(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.define(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.isDefined(j)){ // ID changed because of redundancy if(tempSourceIndex2.isDefined(tempSourceIndex(j))){ // ID changed also because of renumbering if( tempSourceIndex3.isDefined(tempSourceIndex2(tempSourceIndex(j))) ){ // ID also changed because of renaming newSourceIndex2_p.define(j, tempSourceIndex3(tempSourceIndex2(tempSourceIndex(j))) ); // abc } else { // ID changed because of redundancy and renumberning newSourceIndex2_p.define(j, tempSourceIndex2(tempSourceIndex(j))); // ab } } else{ if( tempSourceIndex3.isDefined(tempSourceIndex(j)) ){ // ID changed because of redundancy and renaming newSourceIndex2_p.define(j, tempSourceIndex3(tempSourceIndex(j))); // ac } else { // ID only changed because of redundancy newSourceIndex2_p.define(j, tempSourceIndex(j)); // a } } } else if(tempSourceIndex2.isDefined(j)){ if( tempSourceIndex3.isDefined(tempSourceIndex2(j)) ){ // ID changed because of renumbering and renaming newSourceIndex2_p.define(j, tempSourceIndex3(tempSourceIndex2(j))); // bc } else { // ID only changed because of renumbering newSourceIndex2_p.define(j, tempSourceIndex2(j)); // b } } else if(tempSourceIndex3.isDefined(j)){ // ID only changed because of renaming newSourceIndex2_p.define(j, tempSourceIndex3(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 uInt& rowi, const uInt& 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 (AipsError 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 (AipsError 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 (AipsError 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 (AipsError 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 (AipsError x) { // row has invalid data areEquivalent = True; } // if(!areEquivalent) cout << "not equal transition" << endl; } } return areEquivalent; } Bool MSConcat::obsRowsEquivalent(const MSObservationColumns& obsCol, const uInt& rowi, const uInt& 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; } 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 ROMSSpWindowColumns otherSpwCols(otherSpw); MSSpectralWindow& spw = itsMS.spectralWindow(); MSSpWindowColumns& spwCols = spectralWindow(); const ROTableRow otherSpwRow(otherSpw); TableRow spwRow(spw); const ROMSPolarizationColumns otherPolCols(otherPol); MSPolarization& pol = itsMS.polarization(); MSPolarizationColumns& polCols = polarization(); const ROTableRow otherPolRow(otherPol); TableRow polRow(pol); const ROMSDataDescColumns 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.define(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.define(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.define(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-2.4.1/ms/MSOper/MSConcat.h000066400000000000000000000143101321422335000166740ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #ifndef MS_MSCONCAT_H #define MS_MSCONCAT_H #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN class TableDesc; class ROMSMainColumns; class ROMSDataDescColumns; class ROMSSpWindowColumns; class ROMSPolarizationColumns; class MSAntenna; class MSDataDescription; class MSFeed; class MSField; class MSPolarization; class MSSpectralWindow; template class Block; // A class with functions for concatenating MeasurementSets // // // // //
      • SomeClass //
      • SomeOtherClass //
      • some concept // // // // // // // // // // // // // // // //
      • //
      • // // // //
      • //
      • // // // //
      • add this feature //
      • fix this bug //
      • start discussion of this possible extension // class MSConcat: public MSColumns { public: MSConcat(MeasurementSet& ms); void virtualconcat(MeasurementSet& otherMS, const Bool checkShapeAndCateg=True, const String& obsidAndScanTableName=""); 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 ROMSDataDescColumns& ddCols, const ROMSSpWindowColumns& spwCols, const ROMSPolarizationColumns& polCols, uInt whichShape); void checkShape(const IPosition& otherShape) const; void checkCategories(const ROMSMainColumns& otherCols) const; Bool checkEphIdInField(const ROMSFieldColumns& otherFldCol) const; Bool copyPointing(const MSPointing& otherPoint, const Block& newAntIndices); Bool copyPointingB(MSPointing& otherPoint, const Block& newAntIndices); Int copyObservation(const MSObservation& otherObs, const Bool remRedunObsId=True); // by default remove redundant observation 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 uInt& rowi, const uInt& rowj, const Bool dontTestDirection=False, const Bool dontTestTransAndRest=False); Bool obsRowsEquivalent(const MSObservationColumns& obsCol, const uInt& rowi, const uInt& rowj); void updateModelDataKeywords(MeasurementSet& ms); MeasurementSet itsMS; IPosition itsFixedShape; Quantum itsFreqTol; Quantum itsDirTol; Float itsWeightScale; Bool itsRespectForFieldName; Vector itsChanReversed; SimpleOrderedMap newSourceIndex_p; SimpleOrderedMap newSourceIndex2_p; SimpleOrderedMap newSPWIndex_p; SimpleOrderedMap newObsIndexA_p; SimpleOrderedMap newObsIndexB_p; SimpleOrderedMap otherObsIdsWithCounterpart_p; SimpleOrderedMap solSystObjects_p; Bool doSource_p; Bool doSource2_p; Bool doSPW_p; Bool doObsA_p; Bool doObsB_p; }; template Bool areEQ(const ROScalarColumn& col, uInt row_i, uInt 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 ROArrayColumn& col, uInt row_i, uInt 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); Int ni = arr_i.nelements(); Int nj = arr_j.nelements(); if( (ni==0 && nj==0) || // no data is regarded as equal allEQ(arr_i, arr_j)){ rval = True; } return rval; } } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/ms/MSOper/MSDerivedValues.cc000066400000000000000000000260741321422335000203770ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 ROMSAntennaColumns& 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)){ ROMSColumns 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 ROMSAntennaColumns; 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"); // ROMSColumns 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 ROMSAntennaColumns& 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-2.4.1/ms/MSOper/MSFlagger.cc000066400000000000000000001006261321422335000172000ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #include #include #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,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 ROArrayColumn 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 { { ROArrayColumn 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 ROScalarColumn 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 Int maxRow=1000000/(numCorr*numChan); // of order 1 MB chunks ROArrayColumn 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); Int nRow=tab.nrow(); ROScalarColumn flagRowCol(tab,MS::columnName(MS::FLAG_ROW)); Array flagCube; Vector flagRowVec; for (Int i=0; i<=(nRow/maxRow); i+=maxRow) { Int 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 (Int 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 Int 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))); Int nRow=tab.nrow(); Array flagCube; Vector flagRowVec; Slicer slicer(Slice(level,1),Slice(0,numCorr),Slice(0,numChan)); for (Int i=0; i<=(nRow/maxRow); i+=maxRow) { Int n=min(maxRow,nRow-maxRow*i); if (n tmp(flagHis.reform(IPosition(3,numCorr,numChan,n))); ref.reference(tmp); } Vector rows(n); indgen(rows,uInt(i*maxRow)); Table sel=tab(rows); ArrayColumn flagHisCol(sel,MS::columnName(MS::FLAG_CATEGORY)); ROArrayColumn flagCol(sel,MS::columnName(MS::FLAG)); ROScalarColumn flagRowCol(sel,MS::columnName(MS::FLAG_ROW)); flagCol.getColumn(flagCube,True); flagRowCol.getColumn(flagRowVec,True); ref=flagCube; for (Int 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 Int 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 (Int i=0; i<=nRow/maxRow; i++) { Int n=min(maxRow,nRow-i*maxRow); Vector rows(n); indgen(rows,uInt(i*maxRow)); Table sel=tab(rows); ROArrayColumn 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 (Int 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 namespace casacore { //# NAMESPACE CASACORE - BEGIN template class Array; 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-2.4.1/ms/MSOper/MSKeys.cc000066400000000000000000000123601321422335000165410ustar00rootroot00000000000000//# 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: aips2-request@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-2.4.1/ms/MSOper/MSKeys.h000066400000000000000000000077111321422335000164070ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/ms/MSOper/MSLister.cc000066400000000000000000001534771321422335000171070ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# #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 ROMSSpWindowColumns msSpWinC(pMS_p->spectralWindow()); ROMSPolarizationColumns 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; 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 (AipsError x) { logStream_p << LogOrigin("MSLister","list",WHERE) << LogIO::SEVERE << "Caught exception: " << x.getMesg() << 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(); ROMSDataDescColumns ddCols(pMS_p->dataDescription()); ROScalarColumn ddpolIDs = ddCols.polarizationId(); ROScalarColumn 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 ROMSSpWindowColumns 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 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 ROMSAntennaColumns 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 = (uInt)max(1,(Int)rint(log10(max(uvdist))+0.5)); // order if ( precUVDist_p < 0 ) precUVDist_p = 0; if ( precUVDist_p > 0 ) oUVDist_p++; // add space for decimal oUVW_p = (uInt)max(1,(Int)rint(log10(max(uvw))+0.5)); // 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 = (uInt)max(1,(Int)rint(log10(max(ampl))+0.5)); if ( precAmpl_p < 0 ) precAmpl_p = 3; // mJy if ( precAmpl_p > 0 ) oAmpl_p++; // add space for decimal if (!is_float){ oPhase_p = (uInt)max(1,(Int)rint(abs(log10(max(phase)+0.5)))); 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 = (uInt)max(1,(Int)rint(log10(max(weight))+0.5)); // 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 = (uInt)rint(log10((float)max(fieldid))+0.5); if (doSpW_p) wSpW_p = (uInt)rint(log10((float)max(spwinid))+0.5); 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; ROMSPolarizationColumns 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-2.4.1/ms/MSOper/MSMetaData.cc000066400000000000000000005465201321422335000173200ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: MSMetaData.cc 21590 2015-03-26 19:30:16Z gervandiepen $ #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), _nStates(0), _nACRows(0), _nXCRows(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); ROScalarColumn 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); uInt nrow = _ms->source().nrow(); vector > myvec(nrow); Vector av(2); for (uInt 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; } uInt MSMetaData::nRows() const { return _ms->nrow(); } uInt MSMetaData::nRows(CorrelationType cType) { if (cType == BOTH) { return nRows(); } uInt nACRows, nXCRows; SHARED_PTR > subScanToNACRowsMap, subScanToNXCRowsMap; SHARED_PTR > fieldToNACRowsMap, fieldToNXCRowsMap; _getRowStats( nACRows, nXCRows, subScanToNACRowsMap, subScanToNXCRowsMap, fieldToNACRowsMap, fieldToNXCRowsMap ); if (cType == AUTO) { return nACRows; } else { return nXCRows; } } SHARED_PTR > MSMetaData::getNRowMap(CorrelationType cType) const { uInt nACRows, nXCRows; SHARED_PTR > subScanToNACRowsMap, subScanToNXCRowsMap; SHARED_PTR > fieldToNACRowsMap, fieldToNXCRowsMap; _getRowStats( nACRows, nXCRows, subScanToNACRowsMap, subScanToNXCRowsMap, fieldToNACRowsMap, fieldToNXCRowsMap ); if (cType == AUTO) { return subScanToNACRowsMap; } else if (cType == CROSS) { return subScanToNXCRowsMap; } 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; } uInt 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); uInt nACRows, nXCRows; SHARED_PTR > subScanToNACRowsMap, subScanToNXCRowsMap; 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]; } } uInt MSMetaData::nRows(CorrelationType cType, uInt fieldID) const { _checkField(fieldID); uInt nACRows, nXCRows; SHARED_PTR > subScanToNACRowsMap, subScanToNXCRowsMap; 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; SHARED_PTR > subScanToNACRowsMap, subScanToNXCRowsMap; 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; SHARED_PTR > subScanToNACRowsMap, subScanToNXCRowsMap; 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; SHARED_PTR > subScanToNACRowsMap, subScanToNXCRowsMap; 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; SHARED_PTR > subScanToNACRowsMap, subScanToNXCRowsMap; 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( uInt& nACRows, uInt& 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 { SHARED_PTR > ant1, ant2; _getAntennas(ant1, ant2); SHARED_PTR > scans = _getScans(); SHARED_PTR > fieldIDs = _getFieldIDs(); SHARED_PTR > obsIDs = _getObservationIDs(); 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( uInt& nACRows, uInt& nXCRows, SHARED_PTR >& scanToNACRowsMap, SHARED_PTR >& scanToNXCRowsMap, SHARED_PTR >& fieldToNACRowsMap, 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( SHARED_PTR >& ant1, SHARED_PTR >& ant2 ) const { ant1 = _getMainScalarColumn(MSMainEnums::ANTENNA1); ant2 = _getMainScalarColumn(MSMainEnums::ANTENNA2); } SHARED_PTR > MSMetaData::_getScans() const { return _getMainScalarColumn( MSMainEnums::SCAN_NUMBER ); } SHARED_PTR > MSMetaData::_getObservationIDs() const { return _getMainScalarColumn( MSMainEnums::OBSERVATION_ID ); } SHARED_PTR > MSMetaData::_getArrayIDs() const { return _getMainScalarColumn( MSMainEnums::ARRAY_ID ); } SHARED_PTR > MSMetaData::_getFieldIDs() const { return _getMainScalarColumn( MSMainEnums::FIELD_ID ); } SHARED_PTR > MSMetaData::_getStateIDs() const { 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; } 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( SHARED_PTR > >& subScanToIntentsMap, map >& intentToSubScansMap ) const { // This method is responsible for setting _subScanToIntentsMap and _intentToSubScansMap if (_subScanToIntentsMap && ! _intentToSubScansMap.empty()) { subScanToIntentsMap = _subScanToIntentsMap; intentToSubScansMap = _intentToSubScansMap; return; } 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); SHARED_PTR > > subScanToIntentsMap; std::map > intentToSubScansMap; _getSubScansAndIntentsMaps( subScanToIntentsMap, intentToSubScansMap ); return subScanToIntentsMap->find(subScan)->second; } SHARED_PTR > > MSMetaData::getSubScanToIntentsMap() const { 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]; } uInt MSMetaData::nFields() const { if (_nFields > 0) { return _nFields; } uInt nFields = _ms->field().nrow(); _nFields = nFields; return nFields; } SHARED_PTR > MSMetaData::_getEphemFieldIDs() const { // responsible for setting _ephemFields if (_ephemFields) { return _ephemFields; } ROMSFieldColumns msfc(_ms->field()); ROScalarColumn ephemCol = msfc.ephemerisId(); _ephemFields.reset(new std::set()); 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); ROMSFieldColumns 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); ROMSFieldColumns 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)); SHARED_PTR > scanProps; 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); ROScalarColumn 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); ROScalarColumn 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; } 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 SHARED_PTR > scanProps; SHARED_PTR > subScanProps; _getScanAndSubScanProperties(scanProps, subScanProps, False); return scanProps; } // we don't have it, and we aren't going to want it later return SHARED_PTR >(); } 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 SHARED_PTR > scanProps; SHARED_PTR > subScanProps; _getScanAndSubScanProperties(scanProps, subScanProps, False); return subScanProps; } // we don't have it, and we aren't going to want it later return SHARED_PTR >(); } std::set MSMetaData::getScanKeys() const { if (! _scanKeys.empty()) { return _scanKeys; } std::set scanKeys; 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)); 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); ROScalarColumn 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 { 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(); SHARED_PTR > scans = _getScans(); SHARED_PTR > dataDescIDs = _getDataDescIDs(); SHARED_PTR > times = _getTimes(); 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 = ROScalarColumn( _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 { SHARED_PTR > scans = _getScans(); SHARED_PTR > fields = _getFieldIDs(); SHARED_PTR > arrays = _getArrayIDs(); 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); ROScalarColumn 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; } 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; } 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); 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; } SHARED_PTR > > MSMetaData::_getScanToTimesMap() const { if (_scanToTimesMap && ! _scanToTimesMap->empty()) { return _scanToTimesMap; } SHARED_PTR > scans = _getScans(); SHARED_PTR > obsIDs = _getObservationIDs(); SHARED_PTR > arrayIDs = _getArrayIDs(); Vector::const_iterator curScan = scans->begin(); Vector::const_iterator lastScan = scans->end(); SHARED_PTR > times = _getTimes(); Vector::const_iterator curTime = times->begin(); Vector::const_iterator curObs = obsIDs->begin(); Vector::const_iterator curArray = arrayIDs->begin(); 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 SHARED_PTR > MSMetaData::_getMainScalarColumn( MSMainEnums::PredefinedColumns col ) const { String name = MeasurementSet::columnName(col); ScalarColumn mycol(*_ms, name); SHARED_PTR > v(new Vector()); mycol.getColumn(*v); return v; } SHARED_PTR > MSMetaData::_getTimes() const { return _getMainScalarColumn(MSMainEnums::TIME); } SHARED_PTR > > MSMetaData::_getExposureTimes() const { ScalarQuantColumn col( *_ms, MeasurementSet::columnName(MSMainEnums::EXPOSURE) ); return col.getColumn(); } SHARED_PTR > MSMetaData::_getFlags() const { String flagColName = MeasurementSet::columnName(MSMainEnums::FLAG); return SHARED_PTR >( new ArrayColumn(*_ms, flagColName) ); } std::set MSMetaData::getTimesForScans( std::set scans ) const { std::set times; if (scans.empty()) { SHARED_PTR > allTimes = _getTimes(); times.insert(allTimes->begin(), allTimes->end()); return times; } 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); SHARED_PTR > scanProps; SHARED_PTR > subScanProps; _getScanAndSubScanProperties(scanProps, subScanProps, False); return scanProps->find(scan)->second.times; } std::pair MSMetaData::getTimeRangeForScan( const ScanKey& scanKey ) const { _checkScan(scanKey); SHARED_PTR > scanProps; SHARED_PTR > subScanProps; _getScanAndSubScanProperties(scanProps, subScanProps, False); return scanProps->find(scanKey)->second.timeRange; } SHARED_PTR > > MSMetaData::getScanToTimeRangeMap() const { SHARED_PTR > scanProps; SHARED_PTR > subScanProps; _getScanAndSubScanProperties( scanProps, subScanProps, False ); 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 SHARED_PTR > scanProps; 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); SHARED_PTR > scanProps; 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 { 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()); 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; uInt scanNRows = 0; Record scanRec; _createSubScanRecords( scanRec, scanNRows, antennasForScan, scanKey, subScanProps ); scanRec.define("nrows", scanNRows); scanRec.define("antennas", Vector(antennasForScan.begin(), antennasForScan.size(), 0)); parent.defineRecord("scan=" + String::toString(scanKey.scan), scanRec); ++scanIter; } } void MSMetaData::_createSubScanRecords( Record& parent, uInt& 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)); uInt nrows = props.acRows + props.xcRows; subScanRec.define("nrows", 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", 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; } SHARED_PTR > stateIDs = _getStateIDs(); 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); ROArrayColumn 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); ROArrayColumn 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); ROScalarColumn 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; } SHARED_PTR > obsIDs = _getObservationIDs(); 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); ROScalarColumn 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); 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; } String colName = MSSource::columnName(MSSource::SOURCE_ID); ROScalarColumn id(_ms->source(), colName); colName = MSSource::columnName(MSSource::SPECTRAL_WINDOW_ID); ROScalarColumn spw(_ms->source(), colName); colName = MSSource::columnName(MSSource::NAME); ROScalarColumn name(_ms->source(), colName); colName = MSSource::columnName(MSSource::REST_FREQUENCY); ArrayMeasColumn restfreq(_ms->source(), colName); colName = MSSource::columnName(MSSource::TRANSITION); ROArrayColumn transition(_ms->source(), colName); map mymap; uInt nrows = _ms->source().nrow(); Vector 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.reset(new std::vector(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 uInt mysize = nrows*(2*sizeof(uInt) + sizeof(Double) + 30); if (_cacheUpdated(mysize)) { _sourceInfo = mymap; } return mymap; } map > > MSMetaData::getRestFrequencies() 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.restfreq; ++iter; } 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); ROScalarColumn 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); ROScalarColumn 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); ROScalarColumn 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; SHARED_PTR > > fieldToTimesMap; 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( SHARED_PTR > >& fieldToTimesMap, 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.reset(new std::map >()); timeToFieldsMap.reset(new std::map >()); SHARED_PTR > allFields = _getFieldIDs(); 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(); } SHARED_PTR > > fieldToTimesMap; SHARED_PTR > > timeToFieldsMap; _getFieldsAndTimesMaps( fieldToTimesMap, timeToFieldsMap ); return (*fieldToTimesMap)[fieldID]; } vector MSMetaData::getObservers() const { if (! _observers.empty()) { return _observers; } String colName = MSObservation::columnName(MSObservationEnums::OBSERVER); ROScalarColumn 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); ROScalarColumn 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); ROScalarColumn 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); ROArrayColumn 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); ROArrayColumn 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); ROScalarColumn 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); ROScalarColumn 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.type() != 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; } 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)); 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, Int beginRow, Int nrows ) { v = tp.getColumn(colname, beginRow, nrows, 1).asArrayInt(); } void MSMetaData::_getScalarDoubleColumn( Vector& v, TableProxy& tp, const String& colname, Int beginRow, Int nrows ) { v = tp.getColumn(colname, beginRow, nrows, 1).asArrayDouble(); } void MSMetaData::_getScalarQuantDoubleColumn( Quantum >& v, TableProxy& tp, const String& colname, Int beginRow, Int nrows ) { ScalarQuantColumn mycol(tp.table(), colname); v = Quantum >( tp.getColumn(colname, beginRow, nrows, 1).asArrayDouble(), mycol.getUnits() ); } void MSMetaData::_computeScanAndSubScanProperties( SHARED_PTR >& scanProps, SHARED_PTR >& subScanProps, Bool showProgress ) const { 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.reset(new ProgressMeter(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.reset( new std::map() ); subScanProps.reset( new std::map() ); Int doneRows = 0; uInt msRows = _ms->nrow(); static const uInt rowsInChunk = 10000000; for (uInt 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 ); uInt nchunks = min((uInt)1000, nrows); uInt chunkSize = nrows/nchunks; if (nrows % nchunks > 0) { // integer division nchunks = nrows/chunkSize + 1; } pair, map > *fut = new pair, map >[nchunks]; #pragma omp parallel for for (uInt i=0; iupdate(doneRows); } } _mergeScanProps(scanProps, subScanProps, props); } void MSMetaData::_mergeScanProps( SHARED_PTR >& scanProps, SHARED_PTR >& subScanProps, const std::vector< pair, map > >& props ) const { scanProps.reset( new std::map() ); subScanProps.reset( new std::map() ); 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); uInt 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; uInt 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 uInt& 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, uInt beginRow, uInt 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; uInt 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); } SHARED_PTR > MSMetaData::getSubScanProperties( Bool showProgress ) const { SHARED_PTR > scanProps; SHARED_PTR > subScanProps; _getScanAndSubScanProperties( scanProps, subScanProps, showProgress ); return subScanProps; } SHARED_PTR > MSMetaData::_getScanProperties( Bool showProgress ) const { SHARED_PTR > scanProps; SHARED_PTR > subScanProps; _getScanAndSubScanProperties( scanProps, subScanProps, showProgress ); return scanProps; } void MSMetaData::_getScanAndSubScanProperties( SHARED_PTR >& scanProps, 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; } SHARED_PTR > myssprops; 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, SHARED_PTR >& subScanNACRows, SHARED_PTR >& subScanNXCRows, SHARED_PTR >& fieldNACRows, 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; } SHARED_PTR > ant1, ant2; _getAntennas(ant1, ant2); SHARED_PTR > dataDescIDs = _getDataDescIDs(); SHARED_PTR > scans = _getScans(); SHARED_PTR > fieldIDs = _getFieldIDs(); SHARED_PTR > obsIDs = _getObservationIDs(); 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); 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; } SHARED_PTR > dataDescIDs = _getDataDescIDs(); Vector::const_iterator curDDID = dataDescIDs->begin(); Vector::const_iterator endDDID = dataDescIDs->end(); 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; } SHARED_PTR > allStates = _getStateIDs(); 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 = ROScalarColumn(_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, uInt 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 = ROScalarColumn(*_ms, ant1ColName).get(row); antenna2 = ROScalarColumn(*_ms, ant2ColName).get(row); bool autocorr = (antenna1==antenna2); const String& timeColName = MeasurementSet::columnName(MSMainEnums::TIME); time = ScalarColumn(*_ms, timeColName).get(row); ROMSPointingColumns 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 ROMSPointingColumns& 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); ROScalarColumn 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); ROScalarColumn 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 { 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 { 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 { 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()) { 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" ); } SHARED_PTR > > MSMetaData::_getIntervals() const { ScalarQuantColumn col( *_ms, MeasurementSet::columnName(MSMainEnums::INTERVAL) ); SHARED_PTR > > intervals = col.getColumn(); return intervals; } MSMetaData::ColumnStats MSMetaData::getIntervalStatistics() const { 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; } 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"); ROMSSpWindowColumns 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) ); Vector nss = spwCols.netSideband().getColumn(); Vector name = spwCols.name().getColumn(); Bool myHasBBCNo = hasBBCNo(); Vector bbcno = myHasBBCNo ? spwCols.bbcNo().getColumn() : Vector(); vector freqLimits(2); Vector tmp; vector spwInfo(bws.size()); const static Unit emptyUnit; const static Unit hz("Hz"); uInt nrows = bws.size(); 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 if ( spwInfo[i].nchans == 1 && ! name[i].contains("FULL_RES") ) { avgSpw.insert(i); } else if (spwInfo[i].nchans == 4) { wvrSpw.insert(i); } else { tdmSpw.insert(i); } } 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-2.4.1/ms/MSOper/MSMetaData.h000066400000000000000000001316131321422335000171530ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: MSMetaData.h 21586 2015-03-25 13:46:25Z gervandiepen $ #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; uInt nrows; }; struct ColumnStats { Double max; Double median; Double min; }; typedef std::map > FirstExposureTimeMap; struct SubScanProperties { // number of auto-correlation rows uInt acRows; // number of cross-correlation rows. uInt 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; 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 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 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 uInt nRows() const; uInt nRows(CorrelationType cType); SHARED_PTR > getNRowMap(CorrelationType type) const; uInt nRows( CorrelationType cType, Int arrayID, Int observationID, Int scanNumber, Int fieldID ) const; uInt 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. 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; 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; 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, uInt 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; 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; }; // represents non-primary key data for a SOURCE table row struct SourceProperties { String name; SHARED_PTR > restfreq; 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 uInt _nStates, _nACRows, _nXCRows, _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::map > _antennaNameToIDMap; mutable SHARED_PTR > _scanProperties; mutable 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 SHARED_PTR > _subScanToNACRowsMap, _subScanToNXCRowsMap; mutable SHARED_PTR > _fieldToNACRowsMap, _fieldToNXCRowsMap; mutable std::map > _scanToIntentsMap; mutable 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 SHARED_PTR > > _scanToTimesMap; std::map > _intentToSpwsMap; mutable std::map > _intentToTimesMap; SHARED_PTR > > _fieldToTimesMap; SHARED_PTR > > _timeToFieldsMap; mutable vector _observatoryPositions, _antennaPositions; mutable vector _antennaOffsets; mutable QVD _antennaDiameters; Matrix _uniqueBaselines; Quantity _exposureTime; mutable Double _nUnflaggedACRows, _nUnflaggedXCRows; mutable SHARED_PTR > _unflaggedFieldNACRows, _unflaggedFieldNXCRows; mutable 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 SHARED_PTR > _ephemFields; mutable 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( SHARED_PTR >& scanProps, SHARED_PTR >& subScanProps, Bool showProgress ) const; static void _getScalarIntColumn( Vector& v, TableProxy& table, const String& colname, Int beginRow, Int nrows ); static void _getScalarDoubleColumn( Vector& v, TableProxy& table, const String& colname, Int beginRow, Int nrows ); static void _getScalarQuantDoubleColumn( Quantum >& v, TableProxy& table, const String& colname, Int beginRow, Int nrows ); void _mergeScanProps( SHARED_PTR >& scanProps, SHARED_PTR >& subScanProps, const std::vector< pair, map > >& props ) const; void _createScanRecords( Record& parent, const ArrayKey& arrayKey, const std::map& subScanProps ) const; void _createSubScanRecords( Record& parent, uInt& 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. 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. SHARED_PTR > _generateSubScanPropsIfWanted() const; vector _getAntennaNames( std::map >& namesToIDsMap ) const; vector _getAntennaPositions() const; void _getAntennas( SHARED_PTR >& ant1, SHARED_PTR >& ant2 ) const; SHARED_PTR > _getArrayIDs() const; std::map > _getArrayKeysToSubScanKeys() const; // Uses openmp for parallel processing 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, uInt beginRow, uInt endRow ) const; SHARED_PTR > _getDataDescIDs() const; // get the field IDs of ephemeris objects SHARED_PTR > _getEphemFieldIDs() const; SHARED_PTR > > _getExposureTimes() const; 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( SHARED_PTR > >& fieldToTimesMap, SHARED_PTR > >& timesToFieldMap ); SHARED_PTR > _getFlags() const; std::map > _getIntentsToTimesMap() const; SHARED_PTR > > _getIntervals() const; SHARED_PTR > _getObservationIDs() const; SHARED_PTR > _getScans() const; vector > _getSpwToIntentsMap(); SHARED_PTR > _getStateIDs() const; SHARED_PTR > _getTimes() const; //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 ROMSPointingColumns& pCols, const Int& index, const Double& time ) const; //map _getMeanExposureTimes() const; vector > _getObservationIDToArrayIDsMap() const; vector _getObservatoryPositions(); void _getRowStats( uInt& nACRows, uInt& nXCRows, std::map*& subScanToNACRowsMap, std::map*& subScanToNXCRowsMap, std::map*& fieldToNACRowsMap, std::map*& fieldToNXCRowsMap ) const; void _getRowStats( uInt& nACRows, uInt& nXCRows, SHARED_PTR >& scanToNACRowsMap, SHARED_PTR >& scanToNXCRowsMap, SHARED_PTR >& fieldToNACRowsMap, SHARED_PTR >& fieldToNXCRowsMap ) const; // get scan properties 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; 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( SHARED_PTR > >& subScanToIntentsMap, std::map >& intentToSubScansMap ) const; void _getScanAndSubScanProperties( SHARED_PTR >& scanProps, 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, SHARED_PTR >& subScanToNACRowsMap, SHARED_PTR >& subScanToNXCRowsMap, SHARED_PTR >& fieldToNACRowsMap, 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 SHARED_PTR > _getMainScalarColumn( MSMainEnums::PredefinedColumns col ) const; }; } #endif casacore-2.4.1/ms/MSOper/MSReader.cc000066400000000000000000000440641321422335000170360ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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), itsTabId(-1), 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.define("ANTENNA1", itsAnt1Id); // ANTENNA2 gets a new number itsTabId.define("ANTENNA2", idCount++); itsAnt2Id = itsTabId("ANTENNA2"); // FEED1 == FEED // FEED is required itsFeed1Id = itsTabId("FEED"); DebugAssert(itsFeed1Id>=0, AipsError); itsTabId.define("FEED1", itsFeed1Id); // FEED2 gets a new number itsTabId.define("FEED2", idCount++); itsFeed2Id = itsTabId("FEED2"); // there are two POINTING lookups, one for each possible antenna // POINTING is a required table itsPointing1Id = itsTabId("POINTING"); DebugAssert(itsPointing1Id>=0, AipsError); itsTabId.define("POINTING1", itsPointing1Id); // POINTING2 gets a new number itsTabId.define("POINTING2", idCount++); itsPointing2Id = itsTabId("POINTING2"); // there are two SYSCAL lookups, one for each possible antenna // SYSCAL is an optional table itsSyscal1Id = itsTabId("SYSCAL"); if (itsSyscal1Id >= 0) { itsTabId.define("SYSCAL1", itsSyscal1Id); // SYSCAL2 gets a new number itsTabId.define("SYSCAL2", idCount++); itsSyscal2Id = itsTabId("SYSCAL2"); } // there are two WEATHER lookups, one for each possible antenna // WEATHER is an optional table itsWeather1Id = itsTabId("WEATHER"); if (itsWeather1Id >= 0) { itsTabId.define("WEATHER1", itsWeather1Id); // WEATHER2 gets a new number itsTabId.define("WEATHER2", idCount++); itsWeather2Id = itsTabId("WEATHER2"); } // And the MAIN table needs a presence in the various blocks itsTabId.define("MAIN", idCount++); itsMainId = itsTabId("MAIN"); // 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("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("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("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("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("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("HISTORY"); if (histId >= 0) { itsTabId.define("HISTORY",-1); handledTab(histId) = True; } // OBSERVATION - not indexed, this is a required table itsObsId = itsTabId("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("POLARIZATION"); DebugAssert(itsPolId>=0, AipsError); itsTabRows[itsPolId] = ROTableRow(itsMS.polarization()); handledTab(itsPolId) = True; // PROCESSOR - not indexed, this is a required table itsProcId = itsTabId("PROCESSOR"); DebugAssert(itsProcId>=0, AipsError); itsTabRows[itsProcId] = ROTableRow(itsMS.processor()); handledTab(itsProcId) = True; // SOURCE - indexed, this is an optional table itsSourceId = itsTabId("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("SPECTRAL_WINDOW"); DebugAssert(itsSpwId>=0, AipsError); itsTabRows[itsSpwId] = ROTableRow(itsMS.spectralWindow()); handledTab(itsSpwId) = True; // STATE - not indexed, this is an optional table itsStateId = itsTabId("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 (uInt i=0; i= 0) { String tabName = itsTabId.getKey(i); 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(uInt 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 && uInt(itsRowNumbers[itsMainId]) == 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 Int 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; Int 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; Int 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; Int 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; Int 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; Int 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; Int 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(uInt 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. Int 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; ROMSColumns itsMSCols; // This possibly saves some time, Units of seconds Unit itsSecUnit; MSValidIds itsIds; // this maps table name to an index used throughout this class SimpleOrderedMap 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-2.4.1/ms/MSOper/MSSummary.cc000066400000000000000000002047431321422335000172730ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: MSSummary.cc 21578 2015-03-18 15:01:43Z gervandiepen $ //# #include #include #include #include #include #include #include #include #include #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 (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 // Int 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.reset(new MSMetaData(&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); ROMSMainColumns 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: ROMSFieldColumns field(pMS->field()); Vector fieldnames(field.name().getColumn()); nVisPerField_.resize(fieldnames.nelements()); nVisPerField_=0; // Observing Mode (State Table) ROMSStateColumns state(pMS->state()); Vector obsModes(state.obsMode().getColumn()); // Spw Ids ROMSDataDescColumns 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 set allArrayKeys = uniqueArrayKeys(_msmd->getScanKeys()); set::const_iterator iter = allArrayKeys.begin(); set::const_iterator end = allArrayKeys.end(); SHARED_PTR > > scanToTRMap = _msmd->getScanToTimeRangeMap(); SHARED_PTR > ssprops = _msmd->getSubScanProperties(True); SHARED_PTR > > ssToIntents = _msmd->getSubScanToIntentsMap(); 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; } set subScans = _msmd->getSubScanKeys(*iter); os.output().precision(3); Double lastday = 0; set::const_iterator siter = subScans.begin(); set::const_iterator send = subScans.end(); uInt subsetscan = 0; Int lastscan = 0; for (; siter != send; ++siter) { const MSMetaData::SubScanProperties& props = ssprops->find(*siter)->second; Int nrow = props.acRows + props.xcRows; Int thisscan = siter->scan; 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 ROMSColumns msc(*pMS); Double startTime, stopTime; minMax(startTime, stopTime, msc.time().getColumn()); MVTime startMVT(startTime/86400.0), stopMVT(stopTime/86400.0); ROMSMainColumns 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: ROMSFieldColumns field(pMS->field()); Vector fieldnames(field.name().getColumn()); nVisPerField_.resize(fieldnames.nelements()); nVisPerField_=0; // Spw Ids ROMSDataDescColumns 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: ROTableVector obsidcol(obsarrtab,"OBSERVATION_ID"); ROTableVector 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); Int 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()); Int nrow=t.nrow(); // relevant columns ROTableVector timecol(t,"TIME"); ROTableVector inttim(t,"EXPOSURE"); ROTableVector scncol(t,"SCAN_NUMBER"); ROTableVector fldcol(t,"FIELD_ID"); ROTableVector ddicol(t,"DATA_DESC_ID"); ROTableVector 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 (Int 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].type() != 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 ROMSFeedColumns 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 (uInt 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.getRefPtr()->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 (uInt 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 ROScalarColumn &theTimes((msHis.time())); const ROScalarColumn &messOrigin((msHis.origin())); const ROScalarColumn &messString((msHis.message())); const ROScalarColumn &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 ROMSSourceColumns 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 (uInt 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 ROMSSpWindowColumns 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 (uInt row=0; rowspectralWindow()); // Create a MS-data_desc-columns object ROMSDataDescColumns msDDC(pMS->dataDescription()); // Create a MS-polin-columns object ROMSPolarizationColumns 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()); uInt 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 (uInt 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 ROMSSysCalColumns 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 ROMSWeatherColumns 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 : (Int)pMS->doppler().nrow()); tableRows(4) = pMS->feed().nrow(); tableRows(5) = pMS->field().nrow(); tableRows(6) = pMS->flagCmd().nrow(); tableRows(7) = (pMS->freqOffset().isNull() ? -1 : (Int)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 : (Int)pMS->source().nrow()); tableRows(14) = pMS->spectralWindow().nrow(); tableRows(15) = pMS->state().nrow(); tableRows(16) = (pMS->sysCal().isNull() ? -1 : (Int)pMS->sysCal().nrow()); tableRows(17) = (pMS->weather().isNull() ? -1 : (Int)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-2.4.1/ms/MSOper/MSSummary.h000066400000000000000000000155151321422335000171320ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# #ifndef MS_MSSUMMARY_H #define MS_MSSUMMARY_H #include #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 (SHARED_PTR msmd); // Destructor ~MSSummary(); // Retrieve number of rows Int 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; 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-2.4.1/ms/MSOper/MSValidIds.cc000066400000000000000000000116031321422335000173240ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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 ROMSColumns(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(uInt rownr) const { Int result = -1; if (checkRow(rownr) && romsCols_p) { result = checkResult(romsCols_p->antenna1()(rownr), ms_p.antenna()); } return result; } Int MSValidIds::antenna2(uInt rownr) const { Int result = -1; if (checkRow(rownr) && romsCols_p) { result = checkResult(romsCols_p->antenna2()(rownr), ms_p.antenna()); } return result; } Int MSValidIds::dataDescId(uInt rownr) const { Int result = -1; if (checkRow(rownr) && romsCols_p) { result = checkResult(romsCols_p->dataDescId()(rownr), ms_p.dataDescription()); } return result; } Int MSValidIds::fieldId(uInt rownr) const { Int result = -1; if (checkRow(rownr) && romsCols_p) { result = checkResult(romsCols_p->fieldId()(rownr), ms_p.field()); } return result; } Int MSValidIds::observationId(uInt rownr) const { Int result = -1; if (checkRow(rownr) && romsCols_p) { result = checkResult(romsCols_p->observationId()(rownr), ms_p.observation()); } return result; } Int MSValidIds::processorId(uInt rownr) const { Int result = -1; if (checkRow(rownr) && romsCols_p) { result = checkResult(romsCols_p->processorId()(rownr), ms_p.processor()); } return result; } Int MSValidIds::stateId(uInt rownr) const { Int result = -1; if (checkRow(rownr) && romsCols_p) { result = checkResult(romsCols_p->stateId()(rownr), ms_p.state()); } return result; } Int MSValidIds::polarizationId(uInt rownr) const { Int result = dataDescId(rownr); if (result >= 0) { result = checkResult(romsCols_p->dataDescription().polarizationId()(result), ms_p.polarization()); } return result; } Int MSValidIds::spectralWindowId(uInt rownr) const { Int result = dataDescId(rownr); if (result >= 0) { result = checkResult(romsCols_p->dataDescription().spectralWindowId()(result), ms_p.spectralWindow()); } return result; } Int MSValidIds::dopplerId(uInt 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(uInt 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-2.4.1/ms/MSOper/MSValidIds.h000066400000000000000000000112401321422335000171630ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #ifndef MS_MSVALIDIDS_H #define MS_MSVALIDIDS_H #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward declarations class ROMSColumns; // // // // // // //
      • 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(uInt rownr) const; Int antenna2(uInt rownr) const; Int dataDescId(uInt rownr) const; Int fieldId(uInt rownr) const; Int observationId(uInt rownr) const; Int processorId(uInt rownr) const; Int stateId(uInt 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(uInt 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(uInt 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(uInt 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(uInt rownr) const; // private: MeasurementSet ms_p; ROMSColumns *romsCols_p; Bool hasDoppler_p, hasSource_p; void clear(); Int checkResult(Int testResult, const Table &mstable) const { return (testResult < 0 || uInt(testResult) >= mstable.nrow()) ? -1 : testResult;} Bool checkRow(uInt rownr) const {return rownr < ms_p.nrow();} }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/ms/MSOper/NewMSSimulator.cc000066400000000000000000001726351321422335000202730ustar00rootroot00000000000000 //# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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 #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=new ROMSColumns(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) : ms_p(0), 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=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) : ms_p(0), dataAcc_p(), scratchDataAcc_p(), sigmaAcc_p(), flagAcc_p(), maxData_p(2e9) { LogIO os(LogOrigin("NewMSSimulator", "NewMSSimulator(MeasurementSet& theMS)", WHERE)); defaults(); ms_p = 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); } MeasurementSet * 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); Int 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() { if(ms_p) delete ms_p; ms_p=0; } 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; Int 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; Int 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/C::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.getRefPtr()->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-2.4.1/ms/MSOper/NewMSSimulator.h000066400000000000000000000227421321422335000201260ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef MS_NEWMSSIMULATOR_H #define MS_NEWMSSIMULATOR_H //# Includes #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"); MeasurementSet * 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; MeasurementSet* 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-2.4.1/ms/MSOper/test/000077500000000000000000000000001321422335000160345ustar00rootroot00000000000000casacore-2.4.1/ms/MSOper/test/CMakeLists.txt000066400000000000000000000004451321422335000205770ustar00rootroot00000000000000set (tests tMSDerivedValues tMSKeys tMSMetaData tMSReader tMSSummary ) foreach (test ${tests}) add_executable (${test} ${test}.cc) target_link_libraries (${test} casa_ms) add_test (${test} ${CMAKE_SOURCE_DIR}/cmake/cmake_assay ./${test}) add_dependencies(check ${test}) endforeach (test) casacore-2.4.1/ms/MSOper/test/tMSDerivedValues.cc000066400000000000000000000103241321422335000215310ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 AipsError& x) { cerr << "Exception : " << x.getMesg() << endl; return 1; } return 0; } casacore-2.4.1/ms/MSOper/test/tMSMetaData.cc000066400000000000000000003420501321422335000204530ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: tMSMetaData.cc 21578 2015-03-18 15:01:43Z gervandiepen $ #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 ( std::set::const_iterator intent=uniqueIntents.begin(); intent!=uniqueIntents.end(); ++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) == 24, 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[] = {1, 3, 5, 7, 9, 11, 13, 15}; exp.insert(myints, myints+8); 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); 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 getExposuresForTimes()" << endl; std::map exposuresForTimes = md.getExposuresForTimes(); for ( std::map::const_iterator iter=exposuresForTimes.begin(); iter!=exposuresForTimes.end(); ++iter ) { cout << std::setprecision(11) << iter->first << ", " << iter->second << endl; } } */ { cout << "*** Test getIntentsForField()" << endl; uInt nFields = md.nFields(); 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); } } { 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; 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 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; map > > rfs = md.getRestFrequencies(); map > >::const_iterator iter = rfs.begin(); 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; map > > rfs = md.getTransitions(); map > >::const_iterator iter = rfs.begin(); 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 AipsError& x) { thrown = True; } AlwaysAssert(thrown, AipsError); sskey.scan = 1; MSMetaData::SubScanProperties props = md.getSubScanProperties(sskey); AlwaysAssert(props.acRows + props.xcRows == 367, AipsError); 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(); 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; 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; SHARED_PTR > both = md.getNRowMap(MSMetaData::BOTH); AlwaysAssert(both->find(key)->second == 367, AipsError); SHARED_PTR > ac = md.getNRowMap(MSMetaData::AUTO); AlwaysAssert(ac->find(key)->second == 51, AipsError); 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; 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 preload constructor" << endl; MSMetaDataPreload md(ms); testIt(md); */ 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 AipsError& x) { cerr << "Exception : " << x.getMesg() << endl; return 1; } return 0; } casacore-2.4.1/ms/MSOper/test/tMSMetaData.run000077500000000000000000000003331321422335000206700ustar00rootroot00000000000000#!/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-2.4.1/ms/MSOper/test/tMSReader.cc000066400000000000000000000045161321422335000201770ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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 (AipsError x) { cerr << "Exception : " << x.getMesg() << endl; } return 0; } casacore-2.4.1/ms/MSOper/test/tMSReader.run000066400000000000000000000010461321422335000204110ustar00rootroot00000000000000#!/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-2.4.1/ms/MSOper/test/tMSSummary.cc000066400000000000000000000047601321422335000204330ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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 AipsError& x) { cout << x.getMesg() << endl; } return 0; } casacore-2.4.1/ms/MSOper/test/tMSSummary.in000066400000000000000000005176651321422335000204710ustar00rootroot00000000000000‹Ò«æ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-2.4.1/ms/MSOper/test/tMSSummary.out000066400000000000000000000260231321422335000206510ustar00rootroot00000000000000 *** 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-2.4.1/ms/MSOper/test/tMSSummary.run000077500000000000000000000011051321422335000206430ustar00rootroot00000000000000#!/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-2.4.1/ms/MSSel.h000066400000000000000000000040221321422335000150420ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: MeasurementSets.h 21538 2015-01-07 09:08:57Z gervandiepen $ #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-2.4.1/ms/MSSel/000077500000000000000000000000001321422335000146735ustar00rootroot00000000000000casacore-2.4.1/ms/MSSel/MSAntennaGram.cc000066400000000000000000000226771321422335000176530ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ // 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). #include //# Define register as empty string to avoid warnings in C++11 compilers //# because keyword register is not supported anymore. #define register #include "MSAntennaGram.ycc" // bison output #include "MSAntennaGram.lcc" // flex output // 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-2.4.1/ms/MSSel/MSAntennaGram.ll000066400000000000000000000125301321422335000176600ustar00rootroot00000000000000/* -*- 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: aips2-request@nrao.edu. Postal address: AIPS++ Project Office National Radio Astronomy Observatory 520 Edgemont Road Charlottesville, VA 22903-2475 USA $Id$ */ /* 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-2.4.1/ms/MSSel/MSAntennaGram.yy000066400000000000000000000444401321422335000177170ustar00rootroot00000000000000/*-*- 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: aips2-request@nrao.edu. Postal address: AIPS++ Project Office National Radio Astronomy Observatory 520 Edgemont Road Charlottesville, VA 22903-2475 USA $Id$ */ %{ 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()); if (!($$)) delete $$; $$=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()); if (!$$) delete $$; $$ = 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()); if (!$$) delete $$; $$ = 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()); if (!($$)) delete $$; $$=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()); if (!$$) delete $$; $$ = 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()); if (!$$) delete $$; $$ = new Vector(myMSAI.matchAntennaRegexOrPattern($1,True)); if ((*($$)).nelements() == 0) reportError($1); free($1); MSAntennaParse::thisMSAParser->setComplexity(MSAntennaParse::ANTREGEX); } antidrange: INT // A single antenna index { if (!($$)) delete $$; // // 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; if (!($$)) delete $$; $$ = 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 { if (!($$)) delete $$; $$ = new Vector(*$1); delete $1; } | stationlist COMMA stationid { 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 { if (!($$)) delete $$; $$ = new Vector(*$1); delete $1; } | antlist COMMA antids // AntnnaID, AntnnaID,... { 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 { if (!($$)) delete $$; $$ = 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. { if (!($$)) delete $$; $$ = 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-2.4.1/ms/MSSel/MSAntennaIndex.cc000066400000000000000000000262071321422335000200250ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 ROMSAntennaColumns 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-2.4.1/ms/MSSel/MSAntennaIndex.h000066400000000000000000000073021321422335000176620ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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 ROMSAntennaColumns msAntennaCols_p; // Vector cache of antenna id's Vector antennaIds_p, stationIds_p; Int nrows_p; }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/ms/MSSel/MSAntennaParse.cc000066400000000000000000000340161321422335000200250ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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; MSSelectionErrorHandler* MSAntennaParse::thisMSAErrorHandler = 0; //# 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()); ROMSAntennaColumns 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 MSSelectionErrorHandler* thisMSAErrorHandler; static void cleanupErrorHandler() {if (thisMSAErrorHandler) delete thisMSAErrorHandler;thisMSAErrorHandler=0x0;} 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-2.4.1/ms/MSSel/MSArrayGram.cc000066400000000000000000000120521321422335000173270ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ // 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). #include //# Define register as empty string to avoid warnings in C++11 compilers //# because keyword register is not supported anymore. #define register #include "MSArrayGram.ycc" // bison output #include "MSArrayGram.lcc" // flex output // 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-2.4.1/ms/MSSel/MSArrayGram.ll000066400000000000000000000055441321422335000173610ustar00rootroot00000000000000/* -*- 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: aips2-request@nrao.edu. Postal address: AIPS++ Project Office National Radio Astronomy Observatory 520 Edgemont Road Charlottesville, VA 22903-2475 USA $Id$ */ /* 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-2.4.1/ms/MSSel/MSArrayGram.yy000066400000000000000000000103721321422335000174060ustar00rootroot00000000000000/* -*- 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: aips2-request@nrao.edu. Postal address: AIPS++ Project Office National Radio Astronomy Observatory 520 Edgemont Road Charlottesville, VA 22903-2475 USA $Id$ */ %{ #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-2.4.1/ms/MSSel/MSArrayParse.cc000066400000000000000000000137301321422335000175170ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/ms/MSSel/MSArrayParse.h000066400000000000000000000106711321422335000173620ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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(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-2.4.1/ms/MSSel/MSCorrGram.cc000066400000000000000000000106631321422335000171640ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ // 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). #include //# Define register as empty string to avoid warnings in C++11 compilers //# because keyword register is not supported anymore. #define register #include "MSCorrGram.ycc" // bison output #include "MSCorrGram.lcc" // flex output // 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-2.4.1/ms/MSSel/MSCorrGram.ll000066400000000000000000000077461321422335000172160ustar00rootroot00000000000000/* 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: aips2-request@nrao.edu. Postal address: AIPS++ Project Office National Radio Astronomy Observatory 520 Edgemont Road Charlottesville, VA 22903-2475 USA $Id$ */ /* 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-2.4.1/ms/MSSel/MSCorrGram.yy000066400000000000000000000045401321422335000172350ustar00rootroot00000000000000/* 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: aips2-request@nrao.edu. Postal address: AIPS++ Project Office National Radio Astronomy Observatory 520 Edgemont Road Charlottesville, VA 22903-2475 USA $Id$ */ %{ 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-2.4.1/ms/MSSel/MSCorrParse.cc000066400000000000000000000105341321422335000173450ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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; ROArrayColumn 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"); ROMSPolarizationColumns 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-2.4.1/ms/MSSel/MSCorrParse.h000066400000000000000000000070641321422335000172130ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/ms/MSSel/MSDataDescIndex.cc000066400000000000000000000141221321422335000201020ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 ROMSDataDescColumns 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-2.4.1/ms/MSSel/MSDataDescIndex.h000066400000000000000000000066211321422335000177510ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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 ROMSDataDescColumns msDataDescCols_p; // Vector cache of DataDesc id's Vector dataDescIds_p; Int nrows_p; }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/ms/MSSel/MSDopplerIndex.cc000066400000000000000000000043551321422335000200460ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/ms/MSSel/MSDopplerIndex.h000066400000000000000000000053251321422335000177060ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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-2.4.1/ms/MSSel/MSFeedGram.cc000066400000000000000000000141451321422335000171210ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ // 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). #include //# Define register as empty string to avoid warnings in C++11 compilers //# because keyword register is not supported anymore. #define register #include "MSFeedGram.ycc" // bison output #include "MSFeedGram.lcc" // flex output // 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-2.4.1/ms/MSSel/MSFeedGram.h000066400000000000000000000104651321422335000167640ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/ms/MSSel/MSFeedGram.ll000066400000000000000000000046001321422335000171360ustar00rootroot00000000000000/* -*- 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: aips2-request@nrao.edu. Postal address: AIPS++ Project Office National Radio Astronomy Observatory 520 Edgemont Road Charlottesville, VA 22903-2475 USA $Id$ */ /* 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-2.4.1/ms/MSSel/MSFeedGram.yy000066400000000000000000000124211321422335000171700ustar00rootroot00000000000000/*-*- 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: aips2-request@nrao.edu. Postal address: AIPS++ Project Office National Radio Astronomy Observatory 520 Edgemont Road Charlottesville, VA 22903-2475 USA $Id$ */ %{ 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 { if (!($$)) delete $$; $$ = new Vector(*$1); delete $1; } | feedlist COMMA feedids // feedID, feedID,... { 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; if (!($$)) delete $$; $$ = 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 ROMSFeedColumns(static_cast(table())); } Int MSFeedIndex::compare (const Block& fieldPtrs, const Block& dataPtrs, const Block& dataTypes, Int 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-2.4.1/ms/MSSel/MSFeedIndex.h000066400000000000000000000075621321422335000171510ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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, Int index); private: RecordFieldPtr antennaId_p, feedId_p, spwId_p; // Pointer to FEED columns accessor ROMSFeedColumns* msFeedCols_p; void attachIds(); }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/ms/MSSel/MSFeedParse.cc000066400000000000000000000170061321422335000173040ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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; MSSelectionErrorHandler* MSFeedParse::thisMSFErrorHandler = 0; //# 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 MSSelectionErrorHandler* thisMSFErrorHandler; static void cleanupErrorHandler() {if (thisMSFErrorHandler) delete thisMSFErrorHandler;thisMSFErrorHandler=0x0;} 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-2.4.1/ms/MSSel/MSFieldGram.cc000066400000000000000000000161231321422335000172770ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ // 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). #include //# Define register as empty string to avoid warnings in C++11 compilers //# because keyword register is not supported anymore. #define register #include "MSFieldGram.ycc" // bison output #include "MSFieldGram.lcc" // flex output // 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(MSSelectionFieldError &x) { 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-2.4.1/ms/MSSel/MSFieldGram.ll000066400000000000000000000106271321422335000173240ustar00rootroot00000000000000/* -*- 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: aips2-request@nrao.edu. Postal address: AIPS++ Project Office National Radio Astronomy Observatory 520 Edgemont Road Charlottesville, VA 22903-2475 USA $Id$ */ /* 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-2.4.1/ms/MSSel/MSFieldGram.yy000066400000000000000000000205761321422335000173620ustar00rootroot00000000000000/* -*- 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: aips2-request@nrao.edu. Postal address: AIPS++ Project Office National Radio Astronomy Observatory 520 Edgemont Road Charlottesville, VA 22903-2475 USA $Id$ */ %{ #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()); Vector selectedIDs(myMSFI.maskFieldIDs(myMSFI.validateIndices(*($1)))); $$ = MSFieldParse().selectFieldIds(selectedIDs); m << "Partial or no match for Field ID list " << (*($1)); checkFieldError(selectedIDs, m); delete $1; } ; // // 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; if (!($$)) delete $$; $$=new Vector(myMSFI.matchFieldNameOrCode($1)); //$$=new Vector(myMSAI.matchFieldRegexOrPattern($1)); ostringstream m; m << "No match found for name \"" << $1 << "\""; checkFieldError(*($$), 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 // //MSFieldIndex myMSFI(MSFieldParse::thisMSFParser->msInterface()->field()); //MSFieldIndex myMSFI(MSFieldParse::thisMSFParser->ms()->field()); MSFieldIndex myMSFI(MSFieldParse::thisMSFParser->subTable()); // cerr << "QS: " << $1 << endl; if (!$$) delete $$; $$ = new Vector(myMSFI.matchFieldRegexOrPattern($1)); ostringstream m; m << "No match found for name \"" << $1 << "\""; checkFieldError(*($$), 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 // //MSFieldIndex myMSFI(MSFieldParse::thisMSFParser->msInterface()->field()); //MSFieldIndex myMSFI(MSFieldParse::thisMSFParser->ms()->field()); MSFieldIndex myMSFI(MSFieldParse::thisMSFParser->subTable()); if (!$$) delete $$; $$ = new Vector(myMSFI.matchFieldRegexOrPattern($1,True)); ostringstream m; m << "No match found for \"" << $1 << "\""; checkFieldError(*($$), m); free($1); } ; fieldidrange: INT // A single field index { if (!($$)) delete $$; $$ = 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; if (!($$)) delete $$; $$ = new Vector(fieldids); free($1); free($3); } ; fieldidbounds: LT INT // msInterface()->field()); //MSFieldIndex myMSFI(MSFieldParse::thisMSFParser->ms()->field()); MSFieldIndex myMSFI(MSFieldParse::thisMSFParser->subTable()); if (!($$)) delete $$; Int n=atoi($2); $$ = new Vector(myMSFI.matchFieldIDLT(n)); ostringstream m; m << "No field ID found <" << n; checkFieldError(*($$), m); free($2); } | GT INT // >ID { //MSFieldIndex myMSFI(MSFieldParse::thisMSFParser->msInterface()->field()); //MSFieldIndex myMSFI(MSFieldParse::thisMSFParser->ms()->field()); MSFieldIndex myMSFI(MSFieldParse::thisMSFParser->subTable()); if (!($$)) delete $$; Int n=atoi($2); $$ = new Vector(myMSFI.matchFieldIDGT(n)); ostringstream m; m << "No field ID found >" << n; checkFieldError(*($$), m); free($2); } | GT INT AMPERSAND LT INT // >ID & msInterface()->field()); //MSFieldIndex myMSFI(MSFieldParse::thisMSFParser->ms()->field()); MSFieldIndex myMSFI(MSFieldParse::thisMSFParser->subTable()); if (!($$)) delete $$; Int n0=atoi($2), n1=atoi($5); $$ = new Vector(myMSFI.matchFieldIDGTAndLT(n0,n1)); ostringstream m; m << "No field found in the range [" << n0 << "," << n1 << "]"; checkFieldError(*($$), m); free($2); free($5); } ; fieldidlist: fieldid // A singe field ID { $$ = $1; } | fieldidrange // ID range ( n0-n1 ) { $$ = $1; } | fieldidbounds // >ID, ID & (*$1); delete $1; } | indexlist COMMA fieldidlist { 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 ROMSFieldColumns 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(); } //------------------------------------------------------------------------- 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); 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 ROMSFieldColumns msFieldCols_p; // Vector cache of field id's Vector fieldIds_p; Int nrows_p; }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/ms/MSSel/MSFieldParse.cc000066400000000000000000000163151321422335000174660ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 ; ROMSFieldColumns 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-2.4.1/ms/MSSel/MSFieldParse.h000066400000000000000000000162651321422335000173340ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/ms/MSSel/MSFreqOffIndex.cc000066400000000000000000000046461321422335000177740ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/ms/MSSel/MSFreqOffIndex.h000066400000000000000000000057671321422335000176430ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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-2.4.1/ms/MSSel/MSObsIndex.cc000066400000000000000000000060301321422335000171540ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 ROMSObservationColumns 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-2.4.1/ms/MSSel/MSObsIndex.h000066400000000000000000000061401321422335000170200ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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 ROMSObservationColumns msObservationCols_p; // Vector cache of observation id's Vector observationIds_p; Int nrows_p; }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/ms/MSSel/MSObservationGram.cc000066400000000000000000000160541321422335000205520ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ // 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). #include //# Define register as empty string to avoid warnings in C++11 compilers //# because keyword register is not supported anymore. #define register #include "MSObservationGram.ycc" // bison output #include "MSObservationGram.lcc" // flex output // 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-2.4.1/ms/MSSel/MSObservationGram.ll000066400000000000000000000057261321422335000206000ustar00rootroot00000000000000/* -*- 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: aips2-request@nrao.edu. Postal address: AIPS++ Project Office National Radio Astronomy Observatory 520 Edgemont Road Charlottesville, VA 22903-2475 USA $Id$ */ /* 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-2.4.1/ms/MSSel/MSObservationGram.yy000066400000000000000000000107421321422335000206240ustar00rootroot00000000000000/* -*- 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: aips2-request@nrao.edu. Postal address: AIPS++ Project Office National Radio Astronomy Observatory 520 Edgemont Road Charlottesville, VA 22903-2475 USA $Id$ */ %{ #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-2.4.1/ms/MSSel/MSObservationParse.cc000066400000000000000000000164151321422335000207370ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/ms/MSSel/MSObservationParse.h000066400000000000000000000111531321422335000205730ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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(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-2.4.1/ms/MSSel/MSParse.cc000066400000000000000000000071451321422335000165230ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/ms/MSSel/MSParse.h000066400000000000000000000111411321422335000163540ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/ms/MSSel/MSPointingIndex.cc000066400000000000000000000042701321422335000202240ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/ms/MSSel/MSPointingIndex.h000066400000000000000000000051471321422335000200720ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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-2.4.1/ms/MSSel/MSPolIndex.cc000066400000000000000000000126471321422335000171760ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 ROMSPolarizationColumns 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-2.4.1/ms/MSSel/MSPolIndex.h000066400000000000000000000067321321422335000170360ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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 ROMSPolarizationColumns msPolarizationCols_p; // Vector cache of polarization id's Vector polarizationIds_p; Int nrows_p; }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/ms/MSSel/MSPolnGram.cc000066400000000000000000000104241321422335000171620ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include #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, OrderedMap >& selectedPolnMap, OrderedMap > >& 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.getMesg().c_str())); 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-2.4.1/ms/MSSel/MSPolnGram.h000066400000000000000000000060071321422335000170260ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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, OrderedMap >& selectedPolnMap, OrderedMap > >& 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-2.4.1/ms/MSSel/MSPolnParse.cc000066400000000000000000000400221321422335000173430ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include #include #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; // OrderedMap > MSPolnParse::polList(Vector(0)); //# Constructor //------------------------------------------------------------------------------ // extern const char* strpMSPolnGram; MSPolnParse::MSPolnParse () : MSParse(), node_p(), // node_p(0x0), ddIDList_p(), polMap_p(Vector(0)), setupMap_p(Vector >(0)) { // 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(), polMap_p(Vector(0)), setupMap_p(Vector >(0)) { 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, OrderedMap >& /*polIndexMap*/, Vector& polIndices, Bool addToMap) { Vector rowList; MSPolarization mspol(ms()->polarizationTableName()); ROMSPolarizationColumns 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()); ROMSPolarizationColumns 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.getMesg())); } // // 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) // MapIter > > omi(setupMap_p); for(omi.toStart(); !omi.atEnd(); omi++) if (omi.getVal()[1].nelements() == 0) omi.remove(omi.getKey()); // setupMap_p.remove(omi.getKey()); } 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; if (setupMap_p.isDefined(key)) { Vector t0; v0.resize(0); v0 = setupMap_p(key)[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-2.4.1/ms/MSSel/MSPolnParse.h000066400000000000000000000121341321422335000172100ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef MS_MSPOLNPARSE_H #define MS_MSPOLNPARSE_H //# Includes #include #include #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); OrderedMap > selectedPolnMap() {return polMap_p;} OrderedMap > > 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, OrderedMap >& 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; OrderedMap > polMap_p; OrderedMap > > setupMap_p; void setIDLists(const Int key, const Int ndx, Vector& val); }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/ms/MSSel/MSSSpwErrorHandler.cc000066400000000000000000000041661321422335000206550ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/ms/MSSel/MSSSpwErrorHandler.h000066400000000000000000000046121321422335000205130ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/ms/MSSel/MSScanGram.cc000066400000000000000000000141241321422335000171370ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ // 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). #include //# Define register as empty string to avoid warnings in C++11 compilers //# because keyword register is not supported anymore. #define register #include "MSScanGram.ycc" // bison output #include "MSScanGram.lcc" // flex output // 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-2.4.1/ms/MSSel/MSScanGram.ll000066400000000000000000000055211321422335000171620ustar00rootroot00000000000000/* -*- 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: aips2-request@nrao.edu. Postal address: AIPS++ Project Office National Radio Astronomy Observatory 520 Edgemont Road Charlottesville, VA 22903-2475 USA $Id$ */ /* 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-2.4.1/ms/MSSel/MSScanGram.yy000066400000000000000000000103511321422335000172110ustar00rootroot00000000000000/* -*- 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: aips2-request@nrao.edu. Postal address: AIPS++ Project Office National Radio Astronomy Observatory 520 Edgemont Road Charlottesville, VA 22903-2475 USA $Id$ */ %{ #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-2.4.1/ms/MSSel/MSScanParse.cc000066400000000000000000000160401321422335000173220ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/ms/MSSel/MSScanParse.h000066400000000000000000000107751321422335000171750ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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(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-2.4.1/ms/MSSel/MSSelUtil.h000066400000000000000000000047501321422335000166730ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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-2.4.1/ms/MSSel/MSSelUtil.tcc000066400000000000000000000115011321422335000172050ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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,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,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-2.4.1/ms/MSSel/MSSelUtil2.h000066400000000000000000000050301321422335000167450ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id: HostInfoDarwin.h 21521 2014-12-10 08:06:42Z gervandiepen $ #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, Int 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-2.4.1/ms/MSSel/MSSelUtil2.tcc000066400000000000000000000121271321422335000172740ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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),nChan=data.shape()(1),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 (Int i=0; i void MSSelUtil2::reorderData(Array& data, const Matrix& rowIndex, Int 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) { Int 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-2.4.1/ms/MSSel/MSSelectableMainColumn.h000066400000000000000000000074341321422335000213420ustar00rootroot00000000000000// -*- 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id: HostInfoDarwin.h 21521 2014-12-10 08:06:42Z gervandiepen $ #ifndef MS_MSSELECTABLEMAINCOLUMN_H #define MS_MSSELECTABLEMAINCOLUMN_H #include #include #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 ROArrayColumn& flag() = 0; virtual Bool flagRow(const Int& i) = 0; virtual const ROScalarQuantColumn& exposureQuant() = 0; virtual const ROScalarQuantColumn& 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 ROMSMainColumns(ms_p);} virtual const ROArrayColumn& flag() {return msCols_p->flag();} // virtual Bool flagRow(const Int& i) {return allTrue(msCols_p->flag()(i));} virtual Bool flagRow(const Int& i) {return msCols_p->flagRow()(i);} virtual const ROScalarQuantColumn& exposureQuant() {return msCols_p->exposureQuant();} virtual const ROScalarQuantColumn& timeQuant() {return msCols_p->timeQuant();} virtual const MeasurementSet *asMS(){return static_cast(table());} private: MeasurementSet ms_p; ROMSMainColumns *msCols_p; }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/ms/MSSel/MSSelectableTable.cc000066400000000000000000000033521321422335000204600ustar00rootroot00000000000000// -*- 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/ms/MSSel/MSSelectableTable.h000066400000000000000000000163051321422335000203240ustar00rootroot00000000000000// -*- 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #ifndef MS_MSSELECTABLETABLE_H #define MS_MSSELECTABLETABLE_H #include #include #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-2.4.1/ms/MSSel/MSSelection.cc000066400000000000000000001411321321422335000173710ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //---------------------------------------------------------------------------- #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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, 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(), selectedPolMap_p(Vector(0)), selectedSetupMap_p(Vector >(0)), 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, NO_EXPR), antenna1IDs_p(), antenna2IDs_p(), fieldIDs_p(), spwIDs_p(), scanIDs_p(),ddIDs_p(),baselineIDs_p(), feedPairIDs_p(), selectedTimesList_p(), selectedUVRange_p(),selectedUVUnits_p(), selectedPolMap_p(Vector(0)), selectedSetupMap_p(Vector >(0)), 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(), selectedPolMap_p(Vector(0)), selectedSetupMap_p(Vector >(0)) { // 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): selectedPolMap_p(Vector(0)), selectedSetupMap_p(Vector >(0)) { // 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 hanlder or reset existing // error handlers. If the pointer to error hanlder 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 == NULL) { MSSelectionErrorHandler* tt = new MSSelectionErrorHandler(); setErrorHandler(ANTENNA_EXPR, tt, True); } else MSAntennaParse::thisMSAErrorHandler->reset(); break; } case FEED_EXPR: { if (MSFeedParse::thisMSFErrorHandler == NULL) { MSSelectionErrorHandler* tt = new MSSelectionErrorHandler(); setErrorHandler(FEED_EXPR, tt, True); } else MSFeedParse::thisMSFErrorHandler->reset(); break; } case STATE_EXPR: { if (MSStateParse::thisMSSErrorHandler == NULL) { MSSelectionErrorHandler *tt = new MSSelectionErrorHandler(); setErrorHandler(STATE_EXPR, tt, True); } else MSStateParse::thisMSSErrorHandler->reset(); break; } case SPW_EXPR: { if (MSSpwParse::thisMSSpwErrorHandler == NULL) { MSSSpwErrorHandler* tt = new MSSSpwErrorHandler(); 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(AipsError& x) { runErrorHandler(); deleteNodes(); //deleteErrorHandlers(); throw(x); } 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) { if (mssEH == NULL) MSAntennaParse::thisMSAErrorHandler = mssEH; else MSAntennaParse::thisMSAErrorHandler = mssEH->clone(); } else if (MSAntennaParse::thisMSAErrorHandler == NULL) { if (mssEH == NULL) MSAntennaParse::thisMSAErrorHandler = mssEH; else MSAntennaParse::thisMSAErrorHandler = mssEH->clone(); } break; } case FEED_EXPR: { if (overRide) { if (mssEH == NULL) MSFeedParse::thisMSFErrorHandler = mssEH; else MSFeedParse::thisMSFErrorHandler = mssEH->clone(); } else if (MSFeedParse::thisMSFErrorHandler == NULL) { if (mssEH == NULL) MSFeedParse::thisMSFErrorHandler = mssEH; else MSFeedParse::thisMSFErrorHandler = mssEH->clone(); } break; } case STATE_EXPR: { if (overRide) { MSStateParse::cleanupErrorHandler(); if (mssEH == NULL) MSStateParse::thisMSSErrorHandler = mssEH; else MSStateParse::thisMSSErrorHandler = mssEH->clone(); } else if (MSStateParse::thisMSSErrorHandler == NULL) { if (mssEH == NULL) MSStateParse::thisMSSErrorHandler = mssEH; else MSStateParse::thisMSSErrorHandler = mssEH->clone(); } break; } case SPW_EXPR: { if (overRide) { MSSpwParse::cleanupErrorHandler(); if (mssEH == NULL) MSSpwParse::thisMSSpwErrorHandler = mssEH; else MSSpwParse::thisMSSpwErrorHandler = mssEH->clone(); } else if (MSSpwParse::thisMSSpwErrorHandler == NULL) { if (mssEH == NULL) MSSpwParse::thisMSSpwErrorHandler = mssEH; else MSSpwParse::thisMSSpwErrorHandler = 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, 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 ROMSSpWindowColumns 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 OrderedMap > > corrmap(this->getCorrMap(ms)); // Iterate over the ordered map to fill the slices ConstMapIter > > mi(corrmap); for (mi.toStart(); !mi.atEnd(); mi++) { Int pol=mi.getKey(); Vector corridx=mi.getVal()[0]; Int ncorr=corridx.nelements(); corrslices(pol).resize(ncorr); for (Int i=0;i(MAX_EXPR, 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-2.4.1/ms/MSSel/MSSelection.h000066400000000000000000000635731321422335000172470ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #ifndef MS_MSSELECTION_H #define MS_MSSELECTION_H #include #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). 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 OrderedMap > 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 OrderedMap > > 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; OrderedMap > selectedPolMap_p; OrderedMap > > selectedSetupMap_p; Int maxScans_p, maxObs_p, maxArray_p; Bool isMS_p,toTENCalled_p; }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/ms/MSSel/MSSelectionError.cc000066400000000000000000000202241321422335000204010ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 () throw() {} 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 () throw() {} MSSelectionNullExpr::MSSelectionNullExpr (const String& str,Category c) : MSSelectionError(str,c) {} MSSelectionNullExpr::~MSSelectionNullExpr () throw() {} MSSelectionNullTEN::MSSelectionNullTEN (const String& str,Category c) : MSSelectionError(str,c) {} MSSelectionNullTEN::~MSSelectionNullTEN () throw() {} // //------------------------Time selection expression parser exceptions---------------- // MSSelectionTimeError::MSSelectionTimeError (const String& str,Category c) : MSSelectionError(str,c) {} MSSelectionTimeError::~MSSelectionTimeError () throw() {} MSSelectionTimeParseError::MSSelectionTimeParseError (const String& str,Category c) : MSSelectionTimeError(str,c) {} MSSelectionTimeParseError::~MSSelectionTimeParseError () throw() {} // //------------------------Baseline selection expression parser exceptions------------ // MSSelectionAntennaError::MSSelectionAntennaError (const String& str,Category c) : MSSelectionError(str,c) {} MSSelectionAntennaError::~MSSelectionAntennaError () throw() {} MSSelectionAntennaParseError::MSSelectionAntennaParseError (const String& str,Category c) : MSSelectionAntennaError(str,c) {} MSSelectionAntennaParseError::~MSSelectionAntennaParseError () throw() {} // //------------------------Field selection expression parser exceptions--------------- // MSSelectionFieldError::MSSelectionFieldError (const String& str,Category c) : MSSelectionError(str,c) {} MSSelectionFieldError::~MSSelectionFieldError () throw() {} MSSelectionFieldParseError::MSSelectionFieldParseError (const String& str,Category c) : MSSelectionFieldError(str,c) {} MSSelectionFieldParseError::~MSSelectionFieldParseError () throw() {} MSSelectionFieldWarning::MSSelectionFieldWarning (const String& str,Category c) : MSSelectionFieldError(str,c) {} MSSelectionFieldWarning::~MSSelectionFieldWarning () throw() {} // //------------------------UVDist selection expression parser exceptions-------------- // MSSelectionUvDistError::MSSelectionUvDistError (const String& str,Category c) : MSSelectionError(str,c) {} MSSelectionUvDistError::~MSSelectionUvDistError () throw() {} MSSelectionUvDistParseError::MSSelectionUvDistParseError (const String& str,Category c) : MSSelectionUvDistError(str,c) {} MSSelectionUvDistParseError::~MSSelectionUvDistParseError () throw() {} // //------------------------SPW selection expression parser exceptions----------------- // MSSelectionSpwError::MSSelectionSpwError (const String& str,Category c) : MSSelectionError(str,c) {} MSSelectionSpwError::~MSSelectionSpwError () throw() {} MSSelectionSpwParseError::MSSelectionSpwParseError (const String& str,Category c) : MSSelectionSpwError(str,c) {} MSSelectionSpwParseError::~MSSelectionSpwParseError () throw() {} MSSelectionSpwWarning::MSSelectionSpwWarning (const String& str,Category c) : MSSelectionSpwError(str,c) {} MSSelectionSpwWarning::~MSSelectionSpwWarning () throw() {} // //------------------------Scan selection expression parser exceptions---------------- // MSSelectionScanError::MSSelectionScanError (const String& str,Category c) : MSSelectionError(str,c) {} MSSelectionScanError::~MSSelectionScanError () throw() {} MSSelectionScanParseError::MSSelectionScanParseError (const String& str,Category c) : MSSelectionScanError(str,c) {} MSSelectionScanParseError::~MSSelectionScanParseError () throw() {} // //------------------------Sub-array selection expression parser exceptions----------- // MSSelectionArrayError::MSSelectionArrayError (const String& str,Category c) : MSSelectionError(str,c) {} MSSelectionArrayError::~MSSelectionArrayError () throw() {} MSSelectionArrayParseError::MSSelectionArrayParseError (const String& str,Category c) : MSSelectionArrayError(str,c) {} MSSelectionArrayParseError::~MSSelectionArrayParseError () throw() {} // //----------------------------------------------------------------------------------- // MSSelectionPolnError::MSSelectionPolnError (const String& str,Category c) : MSSelectionError(str,c) {} MSSelectionPolnError::~MSSelectionPolnError () throw() {} MSSelectionPolnParseError::MSSelectionPolnParseError (const String& str,Category c) : MSSelectionPolnError(str,c) {} MSSelectionPolnParseError::~MSSelectionPolnParseError () throw() {} // //----------------------------------------------------------------------------------- // MSSelectionStateError::MSSelectionStateError (const String& str,Category c) : MSSelectionError(str,c) {} MSSelectionStateError::~MSSelectionStateError () throw() {} MSSelectionStateParseError::MSSelectionStateParseError (const String& str,Category c) : MSSelectionStateError(str,c) {} MSSelectionStateParseError::~MSSelectionStateParseError () throw() {} // //----------------------------------------------------------------------------------- // MSSelectionObservationError::MSSelectionObservationError (const String& str,Category c) : MSSelectionError(str,c) {} MSSelectionObservationError::~MSSelectionObservationError () throw() {} MSSelectionObservationParseError::MSSelectionObservationParseError (const String& str,Category c) : MSSelectionObservationError(str,c) {} MSSelectionObservationParseError::~MSSelectionObservationParseError () throw() {} // //----------------------------------------------------------------------------------- // MSSelectionFeedError::MSSelectionFeedError (const String& str,Category c) : MSSelectionError(str,c) {} MSSelectionFeedError::~MSSelectionFeedError () throw() {} MSSelectionFeedParseError::MSSelectionFeedParseError (const String& str,Category c) : MSSelectionFeedError(str,c) {} MSSelectionFeedParseError::~MSSelectionFeedParseError () throw() {} } //# NAMESPACE CASACORE - END casacore-2.4.1/ms/MSSel/MSSelectionError.h000066400000000000000000000255611321422335000202540ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 () throw(); Bool hasMessage; }; // //------------------------------------------------------------------- // class MSSelectionNullSelection : public MSSelectionError { public: MSSelectionNullSelection (const String& message, Category c=GENERAL); ~MSSelectionNullSelection () throw(); }; // //------------------------------------------------------------------- // class MSSelectionNullExpr : public MSSelectionError { public: MSSelectionNullExpr (const String& message, Category c=GENERAL); ~MSSelectionNullExpr () throw(); }; // //------------------------------------------------------------------- // class MSSelectionNullTEN : public MSSelectionError { public: MSSelectionNullTEN (const String& message, Category c=GENERAL); ~MSSelectionNullTEN () throw(); }; // //------------------------------------------------------------------- // class MSSelectionTimeError : public MSSelectionError { public: // Add given message to string "MSSelection time error: ". MSSelectionTimeError (const String& message,Category c=GENERAL); ~MSSelectionTimeError () throw(); }; class MSSelectionTimeParseError: public MSSelectionTimeError { public: MSSelectionTimeParseError (const String& message,Category c=GENERAL); ~MSSelectionTimeParseError () throw(); }; // //------------------------------------------------------------------- // class MSSelectionAntennaError : public MSSelectionError { public: // Add given message to string "MSSelection time error: ". MSSelectionAntennaError (const String& message,Category c=GENERAL); ~MSSelectionAntennaError () throw(); }; // //------------------------------------------------------------------- // class MSSelectionAntennaParseError: public MSSelectionAntennaError { public: MSSelectionAntennaParseError (const String& message,Category c=GENERAL); ~MSSelectionAntennaParseError () throw(); }; // //------------------------------------------------------------------- // class MSSelectionFieldError : public MSSelectionError { public: // Add given message to string "MSSelection time error: ". MSSelectionFieldError (const String& message,Category c=GENERAL); ~MSSelectionFieldError () throw(); }; // //------------------------------------------------------------------- // class MSSelectionFieldParseError: public MSSelectionFieldError { public: MSSelectionFieldParseError (const String& message,Category c=GENERAL); ~MSSelectionFieldParseError () throw(); }; // //------------------------------------------------------------------- // class MSSelectionFieldWarning: public MSSelectionFieldError { public: MSSelectionFieldWarning (const String& message,Category c=GENERAL); ~MSSelectionFieldWarning () throw(); }; // //------------------------------------------------------------------- // class MSSelectionUvDistError : public MSSelectionError { public: // Add given message to string "MSSelection time error: ". MSSelectionUvDistError (const String& message,Category c=GENERAL); ~MSSelectionUvDistError () throw(); }; class MSSelectionUvDistParseError: public MSSelectionUvDistError { public: MSSelectionUvDistParseError (const String& message,Category c=GENERAL); ~MSSelectionUvDistParseError () throw(); }; // //------------------------------------------------------------------- // class MSSelectionSpwError : public MSSelectionError { public: // Add given message to string "MSSelection time error: ". MSSelectionSpwError (const String& message,Category c=GENERAL); ~MSSelectionSpwError () throw(); }; class MSSelectionSpwParseError: public MSSelectionSpwError { public: MSSelectionSpwParseError (const String& message,Category c=GENERAL); ~MSSelectionSpwParseError () throw(); }; class MSSelectionSpwWarning: public MSSelectionSpwError { public: MSSelectionSpwWarning (const String& message,Category c=GENERAL); ~MSSelectionSpwWarning () throw(); }; // //------------------------------------------------------------------- // class MSSelectionScanError : public MSSelectionError { public: // Add given message to string "MSSelection time error: ". MSSelectionScanError (const String& message,Category c=GENERAL); ~MSSelectionScanError () throw(); }; class MSSelectionScanParseError: public MSSelectionScanError { public: MSSelectionScanParseError (const String& message,Category c=GENERAL); ~MSSelectionScanParseError () throw(); }; class MSSelectionScanWarning: public MSSelectionScanError { public: MSSelectionScanWarning (const String& message,Category c=GENERAL); ~MSSelectionScanWarning () throw(); }; // //------------------------------------------------------------------- // class MSSelectionArrayError : public MSSelectionError { public: // Add given message to string "MSSelection time error: ". MSSelectionArrayError (const String& message,Category c=GENERAL); ~MSSelectionArrayError () throw(); }; class MSSelectionArrayParseError: public MSSelectionArrayError { public: MSSelectionArrayParseError (const String& message,Category c=GENERAL); ~MSSelectionArrayParseError () throw(); }; class MSSelectionArrayWarning: public MSSelectionArrayError { public: MSSelectionArrayWarning (const String& message,Category c=GENERAL); ~MSSelectionArrayWarning () throw(); }; // //------------------------------------------------------------------- // class MSSelectionPolnError : public MSSelectionError { public: // Add given message to string "MSSelection time error: ". MSSelectionPolnError (const String& message,Category c=GENERAL); ~MSSelectionPolnError () throw(); }; class MSSelectionPolnParseError: public MSSelectionPolnError { public: MSSelectionPolnParseError (const String& message,Category c=GENERAL); ~MSSelectionPolnParseError () throw(); }; class MSSelectionPolnWarning: public MSSelectionPolnError { public: MSSelectionPolnWarning (const String& message,Category c=GENERAL); ~MSSelectionPolnWarning () throw(); }; // //------------------------------------------------------------------- // class MSSelectionStateError : public MSSelectionError { public: // Add given message to string "MSSelection time error: ". MSSelectionStateError (const String& message,Category c=GENERAL); ~MSSelectionStateError () throw(); }; class MSSelectionStateParseError: public MSSelectionStateError { public: MSSelectionStateParseError (const String& message,Category c=GENERAL); ~MSSelectionStateParseError () throw(); }; class MSSelectionStateWarning: public MSSelectionStateError { public: MSSelectionStateWarning (const String& message,Category c=GENERAL); ~MSSelectionStateWarning () throw(); }; // //------------------------------------------------------------------- // class MSSelectionObservationError : public MSSelectionError { public: // Add given message to string "MSSelection time error: ". MSSelectionObservationError (const String& message,Category c=GENERAL); ~MSSelectionObservationError () throw(); }; class MSSelectionObservationParseError: public MSSelectionObservationError { public: MSSelectionObservationParseError (const String& message,Category c=GENERAL); ~MSSelectionObservationParseError () throw(); }; class MSSelectionObservationWarning: public MSSelectionObservationError { public: MSSelectionObservationWarning (const String& message,Category c=GENERAL); ~MSSelectionObservationWarning () throw(); }; // //------------------------------------------------------------------- // class MSSelectionFeedError : public MSSelectionError { public: // Add given message to string "MSSelection time error: ". MSSelectionFeedError (const String& message,Category c=GENERAL); ~MSSelectionFeedError () throw(); }; class MSSelectionFeedParseError: public MSSelectionFeedError { public: MSSelectionFeedParseError (const String& message,Category c=GENERAL); ~MSSelectionFeedParseError () throw(); }; // //------------------------------------------------------------------- // String constructMessage(const Int pos, const String& command); } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/ms/MSSel/MSSelectionErrorHandler.cc000066400000000000000000000057071321422335000217100ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include #include #include #include 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-2.4.1/ms/MSSel/MSSelectionErrorHandler.h000066400000000000000000000073761321422335000215560ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef MS_MSSELECTIONERRORHANDLER_H #define MS_MSSELECTIONERRORHANDLER_H //# Includes #include #include #include #include #include using namespace std; 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 vector& getMessages() const { return messageList; } Int nMessages() const { return messageList.size(); } protected: 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-2.4.1/ms/MSSel/MSSelectionKeywords.cc000066400000000000000000000121721321422335000211220ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN MSSelectionKeywords::Field MSSelectionKeywords::field(const String& itemName) { // static map with enum to string mapping for fields static SimpleOrderedMap* fieldMap(0); static Block* reverseMap(0); if (!fieldMap) initMap(fieldMap,reverseMap); const Int* p=fieldMap->isDefined(itemName); return p ? Field(*p) : UNDEFINED; } const String& MSSelectionKeywords::keyword(Field fld) { static SimpleOrderedMap* fieldMap(0); static Block* reverseMap(0); if (!reverseMap) initMap(fieldMap,reverseMap); return fieldMap->getKey((*reverseMap)[fld]); } void MSSelectionKeywords::initMap(SimpleOrderedMap*& fieldMap, Block*& reverseMap) { static Bool initialized(False); static SimpleOrderedMap map(UNDEFINED,NUMBER_KEYWORDS); static Block revMap(NUMBER_KEYWORDS); if (!initialized) { map.define("undefined",UNDEFINED); map.define("amplitude",AMPLITUDE); map.define("corrected_amplitude",CORRECTED_AMPLITUDE); map.define("model_amplitude",MODEL_AMPLITUDE); map.define("ratio_amplitude",RATIO_AMPLITUDE); map.define("residual_amplitude",RESIDUAL_AMPLITUDE); map.define("obs_residual_amplitude",OBS_RESIDUAL_AMPLITUDE); map.define("antenna1",ANTENNA1); map.define("antenna2",ANTENNA2); map.define("antennas",ANTENNAS); map.define("array_id",ARRAY_ID); map.define("axis_info",AXIS_INFO); map.define("chan_freq",CHAN_FREQ); map.define("corr_names",CORR_NAMES); map.define("corr_types",CORR_TYPES); map.define("data",DATA); map.define("corrected_data",CORRECTED_DATA); map.define("model_data",MODEL_DATA); map.define("ratio_data",RATIO_DATA); map.define("residual_data",RESIDUAL_DATA); map.define("obs_residual_data",OBS_RESIDUAL_DATA); map.define("data_desc_id",DATA_DESC_ID); map.define("feed1",FEED1); map.define("feed2",FEED2); map.define("field_id",FIELD_ID); map.define("fields",FIELDS); map.define("flag",FLAG); map.define("flag_row",FLAG_ROW); map.define("flag_sum",FLAG_SUM); map.define("float_data",FLOAT_DATA); map.define("ha",HA); map.define("ifr_number",IFR_NUMBER); map.define("imaginary",IMAGINARY); map.define("corrected_imaginary",CORRECTED_IMAGINARY); map.define("model_imaginary",MODEL_IMAGINARY); map.define("ratio_imaginary",RATIO_IMAGINARY); map.define("residual_imaginary",RESIDUAL_IMAGINARY); map.define("obs_residual_imaginary",OBS_RESIDUAL_IMAGINARY); map.define("last",LAST); map.define("num_corr",NUM_CORR); map.define("num_chan",NUM_CHAN); map.define("phase",PHASE); map.define("corrected_phase",CORRECTED_PHASE); map.define("model_phase",MODEL_PHASE); map.define("ratio_phase",RATIO_PHASE); map.define("residual_phase",RESIDUAL_PHASE); map.define("obs_residual_phase",OBS_RESIDUAL_PHASE); map.define("phase_dir",PHASE_DIR); map.define("real",REAL); map.define("corrected_real",CORRECTED_REAL); map.define("model_real",MODEL_REAL); map.define("ratio_real",RATIO_REAL); map.define("residual_real",RESIDUAL_REAL); map.define("obs_residual_real",OBS_RESIDUAL_REAL); map.define("ref_frequency",REF_FREQUENCY); map.define("rows",ROWS); map.define("scan_number",SCAN_NUMBER); map.define("sigma",SIGMA); map.define("time",TIME); map.define("times",TIMES); map.define("u",U); map.define("v",V); map.define("w",W); map.define("ut",UT); map.define("uvw",UVW); map.define("uvdist",UVDIST); map.define("weight",WEIGHT); for (uInt i=0; i namespace casacore { //# NAMESPACE CASACORE - BEGIN template class SimpleOrderedMap; 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); protected: // This class is purely static, no instances are allowed. MSSelectionKeywords(); MSSelectionKeywords(const MSSelectionKeywords& other); MSSelectionKeywords& operator=(const MSSelectionKeywords& other); // initialization function for the string to enum mapping static void initMap(SimpleOrderedMap*& fieldMap, Block*& reverseMap); }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/ms/MSSel/MSSelectionTools.cc000066400000000000000000000267601321422335000204230ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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 (AipsError& x) { if (mymss==NULL) delete mss; throw(x); } 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 (AipsError& x) { if (mymss == NULL) delete mss; throw(x); } 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(); OrderedMap > polMap=thisSelection.getPolMap(); OrderedMap > > 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-2.4.1/ms/MSSel/MSSelectionTools.h000066400000000000000000000106671321422335000202640ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef MS_MSSELECTIONTOOLS_H #define MS_MSSELECTIONTOOLS_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN 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-2.4.1/ms/MSSel/MSSelector.cc000066400000000000000000001567731321422335000172450ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #include #include #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 ROScalarColumn dd(selms_p,MS::columnName(MS::DATA_DESC_ID)); ROMSDataDescColumns 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; ROMSSpWindowColumns spwc(ms_p.spectralWindow()); ROMSPolarizationColumns 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(spwId_p)); Matrix bandwidth = msc.spectralWindow().resolution().getColumnCells(RefRows(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.asArrayInt(RecordFieldId(i)); if (rows.nelements()>0) { Int n=rows.nelements(); if (oneBased) rows-=1; Vector uRows(n); 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 ROArrayColumn 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,uInt(0),uInt(inc)); tab=selms_p(rows); } else { tab=selms_p; } ROMSColumns 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()/C::_2pi*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 ROArrayColumn 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, Int 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)) { Int nRow=min(maxRow_p,nIterRow-startRow_p); selRows_p.resize(nRow); indgen(selRows_p,uInt(startRow_p),uInt(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(); Int 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,uInt(0),uInt(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 ROArrayColumn& col) const { getAveragedData(avData,flag,col,Slicer(Slice())); } void MSSelector::getAveragedData(Array& avData, const Array& flag, const ROArrayColumn& 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); Int 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.0); wt(mask)=1.0; Array avWt(avData.shape(),0.0); 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 ROArrayColumn& col) const { getAveragedData(avData,flag,col,Slicer(Slice())); } void MSSelector::getAveragedData(Array& avData, const Array& flag, const ROArrayColumn& 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); Int 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.0); wt(mask)=1.0; Array avWt(avData.shape(),0.0); 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 ROArrayColumn& col) const { return getAveragedFlag(avFlag,col,Slicer(Slice())); } Array MSSelector::getAveragedFlag(Array& avFlag, const ROArrayColumn& 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); Int 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(); Int 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 ROArrayColumn& 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); Int nRow=selms_p.nrow(); Bool deleteFlag, deleteRow; const Bool* pFlag=flagRow.getStorage(deleteFlag); const Int* 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); Int nRow=selms_p.nrow(); Bool deleteWeight, deleteRow, deleteRowWeight; const Float* pWeight=weight.getStorage(deleteWeight); const Int* 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, Int 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 Int 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 Vector selRows_p; // range of rows from selms_p returned by getData Int startRow_p, maxRow_p; // start and length of range of rows Bool useIfrDefault_p; }; inline Int 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-2.4.1/ms/MSSel/MSSourceIndex.cc000066400000000000000000000143251321422335000176770ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 ROMSSourceColumns(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; } Vector MSSourceIndex::getRowNumbersOfSourceID(const Int sid){ Vector retval; ColumnsIndex sidIndx(table(), MSSource::columnName(MSSource::SOURCE_ID)); RecordFieldPtr sourceId (sidIndx.accessKey(), MSSource::columnName(MSSource::SOURCE_ID)); *sourceId=sid; retval.resize(); retval=sidIndx.getRowNumbers(); return retval; } 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, Int 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-2.4.1/ms/MSSel/MSSourceIndex.h000066400000000000000000000071131321422335000175360ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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 Vector 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, Int index); private: // Pointer to local ROMSSourceColumns object ROMSSourceColumns* msSourceCols_p; RecordFieldPtr sourceId_p, spwId_p; void attachIds(); }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/ms/MSSel/MSSpWindowIndex.cc000066400000000000000000000142311321422335000202050ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 ROMSSpWindowColumns 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-2.4.1/ms/MSSel/MSSpWindowIndex.h000066400000000000000000000070351321422335000200530ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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 ROMSSpWindowColumns msSpWindowCols_p; // Vector cache of SpWindow id's Vector spWindowIds_p; Int nrows_p; }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/ms/MSSel/MSSpwGram.cc000066400000000000000000000152071321422335000170270ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ // 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). #include //# Define register as empty string to avoid warnings in C++11 compilers //# because keyword register is not supported anymore. #define register #include "MSSpwGram.ycc" // bison output #include "MSSpwGram.lcc" // flex output // 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-2.4.1/ms/MSSel/MSSpwGram.h000066400000000000000000000071121321422335000166650ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/ms/MSSel/MSSpwGram.ll000066400000000000000000000127261321422335000170540ustar00rootroot00000000000000/* -*- 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: aips2-request@nrao.edu. Postal address: AIPS++ Project Office National Radio Astronomy Observatory 520 Edgemont Road Charlottesville, VA 22903-2475 USA $Id$ */ /* 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-2.4.1/ms/MSSel/MSSpwGram.yy000066400000000000000000000333521321422335000171040ustar00rootroot00000000000000/* -*-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: aips2-request@nrao.edu. Postal address: AIPS++ Project Office National Radio Astronomy Observatory 520 Edgemont Road Charlottesville, VA 22903-2475 USA $Id$ */ %{ 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 { if (!($$)) delete $$; $$ = new Vector(0); Int N0=(*($$)).nelements(),N1=4; (*($$)).resize(N0+N1,True); // Resize the existing list for(Int i=N0;ims()->spectralWindow()); MSSpwIndex myMSSI(MSSpwParse::thisMSSParser->subTable()); if (!($$)) delete $$; $$=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()); if (!$$) delete $$; $$ = 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()); if (!$$) delete $$; $$ = 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()); if (!($$)) delete $$; 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()); if (!($$)) delete $$; 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()); if (!($$)) delete $$; 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()); if (!($$)) delete $$; $$ = 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()); if (!($$)) delete $$; 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; */ } ; 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; } ; FullExpr: FullSpec {} | FullExpr COMMA FullSpec {} ; %% casacore-2.4.1/ms/MSSel/MSSpwIndex.cc000066400000000000000000000525471321422335000172200ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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(); } // //------------------------------------------------------------------ // 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; ///Bool begIn=False; ///Bool aftIn=False; spw.resize(); start.resize(); nchan.resize(); Int nmatch=0; for (Int k=0; k < nspw; ++k){ Bool locfound=False; Bool dum; ///Int chanpositive=1; Vector chanfreq=msSpwSubTable_p.chanFreq()(k); ///if (chanfreq.nelements() >1){ ///chanpositive=((chanfreq[1]-chanfreq[0]) > 0.0) ? 1: -1; ///} 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); ///begIn=False; ///aftIn=False; if(f0 > chanfreq(sortIndx[0]) && f0 < chanfreq(sortIndx[nch-1])){ ///begIn=True; locfound=True; } if(f1 > chanfreq(sortIndx[0]) && f1 < chanfreq(sortIndx[nch-1])){ ///aftIn=True; locfound=True; } if(locfound){ ++nmatch; spw.resize(nmatch, True); spw(nmatch-1)=k; start.resize(nmatch, True); nchan.resize(nmatch, True); found=True; Vector chanIn(chanfreq.nelements()); chanIn=-1; Int numMatched=0; //cerr << "f0 " << f0 << " f1 " << f1 << endl; for (uInt kk=0; kk < chanfreq.nelements(); ++kk){ //cerr << kk << " " << chanfreq[kk]+0.5*fabs(chanwidth[kk]) << " " << (chanfreq[kk]-0.5*fabs(chanwidth[kk])) << " " << chanfreq[kk] << endl; if( ((chanfreq[kk]+0.5*fabs(chanwidth[kk])) > f0) && ((chanfreq[kk]-0.5*fabs(chanwidth[kk])) < f1)){ chanIn[numMatched]=kk; ++ numMatched; } } chanIn.resize(numMatched, True); //cerr << "chanIn "<< chanIn << endl; start(nmatch-1)=min(chanIn); nchan(nmatch-1)=max(chanIn)-start(nmatch-1)+1; //cerr << "chanIn "<< chanIn << " start " << start << " nchan " << nchan << endl; /* if(begIn){ Int counter=0; //Use abs as sometimes chanwidth is negative and sometimes not ! while((chanfreq(sortIndx[counter])+0.5*fabs(chanwidth(sortIndx[counter]))) < f0) ++counter; start(nmatch-1)= counter > 0 ? (chanpositive ? counter-1 : counter+1): 0; } else{ start(nmatch-1)=0; } if(aftIn){ Int counter=nch-1; while((chanfreq(sortIndx[counter])-0.5*chanwidth(sortIndx[counter])) > f1) --counter; if(counter <= start(nmatch-1)) nchan(nmatch-1)=1; else nchan(nmatch-1)=counter-start(nmatch-1)+1; } else{ nchan(nmatch-1)=nch-start(nmatch-1); } //if it is reversed sort order reverse start if(sortIndx[0] != 0){ start[nmatch-1] = nch-start[nmatch-1]-nchan(nmatch-1); if(start[nmatch-1] < 0) start[nmatch-1]=0; if((start[nmatch-1]+nchan[nmatch-1]) >= nch) nchan[nmatch-1]=nch-start[nmatch-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; ROArrayColumn chanWidth(msSpwSubTable_p.chanWidth()); ROArrayColumn 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 ROArrayColumn chanWidth(msSpwSubTable_p.chanWidth()); ROArrayColumn 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(i+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); 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 ROMSSpWindowColumns msSpwSubTable_p; // ROMSDataDescColumns msDataDescSubTable_p; enum MODES {EXACT=1, APPROX, RANGE}; Vector spwIDs; }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/ms/MSSel/MSSpwParse.cc000066400000000000000000000233011321422335000172050ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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; MSSelectionErrorHandler* MSSpwParse::thisMSSpwErrorHandler = 0; //# 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) { // ROMSSpWindowColumns msSpwSubTable(ms()->spectralWindow()); // ROMSDataDescColumns msDataDescSubTable(ms()->dataDescription()); ROMSSpWindowColumns msSpwSubTable(spwSubTable_p); ROMSDataDescColumns 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) { // ROMSSpWindowColumns msSpwSubTable(ms()->spectralWindow()); // ROMSDataDescColumns msDataDescSubTable(ms()->dataDescription()); ROMSSpWindowColumns msSpwSubTable(spwSubTable_p); ROMSDataDescColumns 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-2.4.1/ms/MSSel/MSSpwParse.h000066400000000000000000000122251321422335000170520ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 MSSelectionErrorHandler* 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() {if (thisMSSpwErrorHandler) delete thisMSSpwErrorHandler;thisMSSpwErrorHandler=0x0;} 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-2.4.1/ms/MSSel/MSStateGram.cc000066400000000000000000000121151321422335000173310ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ // 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). #include //# Define register as empty string to avoid warnings in C++11 compilers //# because keyword register is not supported anymore. #define register #include "MSStateGram.ycc" // bison output #include "MSStateGram.lcc" // flex output // 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-2.4.1/ms/MSSel/MSStateGram.h000066400000000000000000000062421321422335000171770ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/ms/MSSel/MSStateGram.ll000066400000000000000000000106741321422335000173630ustar00rootroot00000000000000/* -*- 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: aips2-request@nrao.edu. Postal address: AIPS++ Project Office National Radio Astronomy Observatory 520 Edgemont Road Charlottesville, VA 22903-2475 USA $Id$ */ /* 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-2.4.1/ms/MSSel/MSStateGram.yy000066400000000000000000000177041321422335000174160ustar00rootroot00000000000000/* -*- 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: aips2-request@nrao.edu. Postal address: AIPS++ Project Office National Radio Astronomy Observatory 520 Edgemont Road Charlottesville, VA 22903-2475 USA $Id$ */ %{ #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 { if (!$$) delete $$; $$ = new Vector(set_intersection(*$1,*$3)); }; | logicallist AMPERSAND stateid { if (!$$) delete $$; $$ = 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()); if (!($$)) delete $$; $$=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()); if (!$$) delete $$; $$ = 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()); if (!$$) delete $$; $$ = new Vector(myMSSI.matchStateRegexOrPattern($1,True)); ostringstream m; m << "No match found for \"" << $1 << "\""; checkStateError(*($$), m); free($1); } ; stateidrange: INT // A single state index { if (!($$)) delete $$; $$ = 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; } if (!($$)) delete $$; $$ = new Vector(stateids); free($1); free($3); } ; stateidbounds: LT INT // ms()->state()); if (!($$)) delete $$; 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()); if (!($$)) delete $$; 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()); if (!($$)) delete $$; 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 { 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-2.4.1/ms/MSSel/MSStateIndex.h000066400000000000000000000075301321422335000173610ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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 ROMSStateColumns 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-2.4.1/ms/MSSel/MSStateParse.cc000066400000000000000000000063561321422335000175270ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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; //CountedPtr 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-2.4.1/ms/MSSel/MSStateParse.h000066400000000000000000000101271321422335000173600ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef MS_MSSTATEPARSE_H #define MS_MSSTATEPARSE_H //# Includes #include #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 CountedPtr 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() {if (thisMSSErrorHandler) delete thisMSSErrorHandler;thisMSSErrorHandler=0x0;} static void cleanup() {cleanupNode(); cleanupErrorHandler();} private: static TableExprNode* node_p; const String colName; static Vector idList; }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/ms/MSSel/MSSysCalIndex.cc000066400000000000000000000044621321422335000176360ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/ms/MSSel/MSSysCalIndex.h000066400000000000000000000055061321422335000175000ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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-2.4.1/ms/MSSel/MSTableIndex.cc000066400000000000000000000274511321422335000174720ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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(); } Vector MSTableIndex::getRowNumbers() { getInternals(); return lastSearch_p; } uInt 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 uInt *rowPtr = lastSearch_p.getStorage(deleteIt); while (!nearestFound_p && thisElem < nElem) { uInt 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 Vector 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 uInt 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; ROScalarColumn 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 Vector lastSearch_p; // last nearest Int lastNearest_p; Bool nearestFound_p, nearestReady_p; // last known sub-table size uInt 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-2.4.1/ms/MSSel/MSTimeDefinitions.h000066400000000000000000000027451321422335000204060ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/ms/MSSel/MSTimeGram.cc000066400000000000000000000207761321422335000171630ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ // 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). #include //# Define register as empty string to avoid warnings in C++11 compilers //# because keyword register is not supported anymore. #define register #include "MSTimeGram.ycc" // bison output #define yy_scan_chars yy_scan_chars_MSTimeGram #include "MSTimeGram.lcc" // flex output // 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-2.4.1/ms/MSSel/MSTimeGram.ll000066400000000000000000000073741321422335000172040ustar00rootroot00000000000000/* -*- 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: aips2-request@nrao.edu. Postal address: AIPS++ Project Office National Radio Astronomy Observatory 520 Edgemont Road Charlottesville, VA 22903-2475 USA $Id$ */ /* 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-2.4.1/ms/MSSel/MSTimeGram.yy000066400000000000000000000260541321422335000172320ustar00rootroot00000000000000/* -*-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: aips2-request@nrao.edu. Postal address: AIPS++ Project Office National Radio Astronomy Observatory 520 Edgemont Road Charlottesville, VA 22903-2475 USA $Id$ */ %{ 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 s; s=$3.sec; Time time0($1.year,$1.month,$1.day,$1.hour,$1.minute,(Double)$1.sec); Time time1($3.year,$3.month,$3.day,$3.hour,$3.minute,s); Double mjd=time1.modifiedJulianDay()*86400.0; time1 = time0 + mjd; const MEpoch *t0=new MEpoch(MVEpoch(time0.modifiedJulianDay())); const MEpoch *t1=new MEpoch(MVEpoch(time1.modifiedJulianDay())); $$ = 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-2.4.1/ms/MSSel/MSTimeParse.cc000066400000000000000000000373461321422335000173500ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 // ROMSMainColumns. 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 // ROMSMainColumns::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 //ROMSMainColumns 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. // ROScalarQuantColumn 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-2.4.1/ms/MSSel/MSTimeParse.h000066400000000000000000000143441321422335000172030ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/ms/MSSel/MSUvDistGram.cc000066400000000000000000000136431321422335000174760ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ // 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). #include //# Define register as empty string to avoid warnings in C++11 compilers //# because keyword register is not supported anymore. #define register #include "MSUvDistGram.ycc" // bison output #include "MSUvDistGram.lcc" // flex output // 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-2.4.1/ms/MSSel/MSUvDistGram.ll000066400000000000000000000055211321422335000175140ustar00rootroot00000000000000/* -*- 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: aips2-request@nrao.edu. Postal address: AIPS++ Project Office National Radio Astronomy Observatory 520 Edgemont Road Charlottesville, VA 22903-2475 USA $Id$ */ /* 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-2.4.1/ms/MSSel/MSUvDistGram.yy000066400000000000000000000074441321422335000175540ustar00rootroot00000000000000/* 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: aips2-request@nrao.edu. Postal address: AIPS++ Project Office National Radio Astronomy Observatory 520 Edgemont Road Charlottesville, VA 22903-2475 USA $Id: MSUvDistGram.yy 21521 2014-12-10 08:06:42Z gervandiepen $ */ %{ 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-2.4.1/ms/MSSel/MSUvDistParse.cc000066400000000000000000000176501321422335000176640ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 ROMSMainColumns msMainCol(*ms()); ROMSSpWindowColumns msSpwCol(ms()->spectralWindow()); ROMSDataDescColumns 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-2.4.1/ms/MSSel/MSUvDistParse.h000066400000000000000000000103021321422335000175110ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/ms/MSSel/MSWeatherIndex.cc000066400000000000000000000042401321422335000200310ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/ms/MSSel/MSWeatherIndex.h000066400000000000000000000051251321422335000176760ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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-2.4.1/ms/MSSel/test/000077500000000000000000000000001321422335000156525ustar00rootroot00000000000000casacore-2.4.1/ms/MSSel/test/CMakeLists.txt000066400000000000000000000005751321422335000204210ustar00rootroot00000000000000set (tests tMSAntennaGram tMSAntennaGram2 tMSAntennaGram3 tMSCorrGram tMSFeedGram tMSFieldGram tMSScanGram tMSSpwGram tMSTimeGram tMSUvDistGram tMSSelection ) foreach (test ${tests}) add_executable (${test} ${test}.cc) target_link_libraries (${test} casa_ms) add_test (${test} ${CMAKE_SOURCE_DIR}/cmake/cmake_assay ./${test}) add_dependencies(check ${test}) endforeach (test) casacore-2.4.1/ms/MSSel/test/mssel_test_small.ms.tgz000066400000000000000000022445041321422335000224030ustar00rootroot00000000000000‹‘ß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$½ #include #include #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 (AipsError& x) { cout << "ERROR: " << x.getMesg() << endl; return 1; } return 0; } casacore-2.4.1/ms/MSSel/test/tMSAntennaGram.run000066400000000000000000000000421321422335000212130ustar00rootroot00000000000000#! /bin/sh echo "UNTESTED" exit 3 casacore-2.4.1/ms/MSSel/test/tMSAntennaGram2.cc000066400000000000000000000072151321422335000210670ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include #include #include #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 (AipsError& x) { cout << "ERROR: " << x.getMesg() << endl; return 1; } return 0; } casacore-2.4.1/ms/MSSel/test/tMSAntennaGram2.run000066400000000000000000000000421321422335000212750ustar00rootroot00000000000000#! /bin/sh echo "UNTESTED" exit 3 casacore-2.4.1/ms/MSSel/test/tMSAntennaGram3.cc000066400000000000000000000103011321422335000210560ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError& x) { cout << "ERROR: " << x.getMesg() << endl; return 1; } return 0; } casacore-2.4.1/ms/MSSel/test/tMSAntennaGram3.out000066400000000000000000000046101321422335000213060ustar00rootroot0000000000000020&& [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-2.4.1/ms/MSSel/test/tMSCorrGram.cc000066400000000000000000000066641321422335000203350ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include #include #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 (AipsError x) { cout << "ERROR: " << x.getMesg() << endl; return 1; } return 0; } casacore-2.4.1/ms/MSSel/test/tMSCorrGram.run000077500000000000000000000000421321422335000205370ustar00rootroot00000000000000#! /bin/sh echo "UNTESTED" exit 3 casacore-2.4.1/ms/MSSel/test/tMSFeedGram.cc000066400000000000000000000073461321422335000202710ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include #include #include #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 (AipsError& x) { cout << "ERROR: " << x.getMesg() << endl; return 1; } return 0; } casacore-2.4.1/ms/MSSel/test/tMSFieldGram.cc000066400000000000000000000066741321422335000204540ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include #include #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 (AipsError x) { cout << "ERROR: " << x.getMesg() << endl; return 1; } return 0; } casacore-2.4.1/ms/MSSel/test/tMSFieldGram.run000066400000000000000000000000421321422335000206520ustar00rootroot00000000000000#! /bin/sh echo "UNTESTED" exit 3 casacore-2.4.1/ms/MSSel/test/tMSScanGram.cc000066400000000000000000000066331321422335000203100ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include #include #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 (AipsError x) { cout << "ERROR: " << x.getMesg() << endl; return 1; } return 0; } casacore-2.4.1/ms/MSSel/test/tMSScanGram.run000066400000000000000000000000421321422335000205130ustar00rootroot00000000000000#! /bin/sh echo "UNTESTED" exit 3 casacore-2.4.1/ms/MSSel/test/tMSSelection.cc000066400000000000000000000252401321422335000205350ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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.getMesg() << 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.getMesg() << endl; } } // // There should be no tables in the cache outside the scope of the // MSSelection object. // showTableCache(); } casacore-2.4.1/ms/MSSel/test/tMSSelection.out000066400000000000000000001535611321422335000207670ustar00rootroot00000000000000BE: 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 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ###AipsError: 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-2.4.1/ms/MSSel/test/tMSSelection.run000066400000000000000000000200311321422335000207450ustar00rootroot00000000000000#!/bin/sh # ----------------------------------------------------------------------------- # Usage: tMSSelection.run # ----------------------------------------------------------------------------- # This script executes the program tMSSelection to test the # MSSelection module. # # $Id$ #----------------------------------------------------------------------------- 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-2.4.1/ms/MSSel/test/tMSSpwGram.cc000066400000000000000000000070151321422335000201700ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include #include #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 (AipsError x) { cout << "ERROR: " << x.getMesg() << endl; return 1; } return 0; } casacore-2.4.1/ms/MSSel/test/tMSSpwGram.run000066400000000000000000000000421321422335000204000ustar00rootroot00000000000000#! /bin/sh echo "UNTESTED" exit 3 casacore-2.4.1/ms/MSSel/test/tMSTimeGram.cc000066400000000000000000000066641321422335000203260ustar00rootroot00000000000000//# 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: Aips2-Request@Nrao.Edu. //# Postal Address: Aips++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, Va 22903-2475 Usa //# //# $Id$ #include #include #include #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. A read-only version of these classes is also // provided (e.g., ROMSFieldColumns). // // 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 // ROMSColumns 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-2.4.1/ms/MeasurementSets/000077500000000000000000000000001321422335000170345ustar00rootroot00000000000000casacore-2.4.1/ms/MeasurementSets/MSAntenna.cc000066400000000000000000000150451321422335000211740ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: MSAntenna.cc 18093 2004-11-30 17:51:10Z ddebonis $ #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, uInt 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 &, uInt, 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; } void MSAntenna::init() { if (! columnMap_p.ndefined()) { // the PredefinedColumns // DISH_DIAMETER colMapDef(DISH_DIAMETER, "DISH_DIAMETER", TpDouble, "Physical diameter of dish","m",""); // FLAG_ROW colMapDef(FLAG_ROW,"FLAG_ROW",TpBool, "Flag for this row","",""); // MOUNT colMapDef(MOUNT,"MOUNT",TpString, "Mount type e.g. alt-az, equatorial, etc.","",""); // NAME colMapDef(NAME,"NAME",TpString, "Antenna name, e.g. VLA22, CA03","",""); // OFFSET colMapDef(OFFSET,"OFFSET",TpArrayDouble, "Axes offset of mount to FEED REFERENCE point", "m","Position"); // POSITION colMapDef(POSITION,"POSITION",TpArrayDouble, "Antenna X,Y,Z phase reference position","m","Position"); // STATION colMapDef(STATION,"STATION",TpString, "Station (antenna pad) name","",""); // TYPE colMapDef(TYPE,"TYPE", TpString, "Antenna type (e.g. SPACE-BASED)","",""); // Optional columns follow // MEAN_ORBIT colMapDef(MEAN_ORBIT,"MEAN_ORBIT",TpArrayDouble, "Mean Keplerian elements","",""); // ORBIT_ID colMapDef(ORBIT_ID,"ORBIT_ID",TpInt, "index into ORBIT table (ignore if<0)","",""); // PHASED_ARRAY_ID colMapDef(PHASED_ARRAY_ID,"PHASED_ARRAY_ID",TpInt, "index into PHASED_ARRAY table","",""); // PredefinedKeywords // init requiredTableDesc TableDesc requiredTD; // all required keywords uInt i; for (i = UNDEFINED_KEYWORD+1; i <= NUMBER_PREDEFINED_KEYWORDS; i++) { addKeyToDesc(requiredTD, PredefinedKeywords(i)); } // all required columns // First define the columns with fixed size arrays IPosition shape(1,3); ColumnDesc::Option option=ColumnDesc::Direct; addColumnToDesc(requiredTD, OFFSET, shape, option); addColumnToDesc(requiredTD, POSITION, shape, option); // Now define all other columns (duplicates are skipped) for (i = UNDEFINED_COLUMN+1; i <= NUMBER_REQUIRED_COLUMNS; i++) { addColumnToDesc(requiredTD, PredefinedColumns(i)); } requiredTD_p=new TableDesc(requiredTD); } } MSAntenna MSAntenna::referenceCopy(const String& newTableName, const Block& writableColumns) const { return MSAntenna(MSTable:: referenceCopy(newTableName,writableColumns)); } } //# NAMESPACE CASACORE - END casacore-2.4.1/ms/MeasurementSets/MSAntenna.h000066400000000000000000000115541321422335000210370ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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, uInt 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 void init(); private: // required by the need to throw an exception in the destructor Bool hasBeenDestroyed_p; }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/ms/MeasurementSets/MSAntennaColumns.cc000066400000000000000000000317061321422335000225370ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN ROMSAntennaColumns::ROMSAntennaColumns(const MSAntenna& msAntenna): dishDiameter_p(msAntenna, MSAntenna:: columnName(MSAntenna::DISH_DIAMETER)), flagRow_p(msAntenna, MSAntenna::columnName(MSAntenna::FLAG_ROW)), mount_p(msAntenna, MSAntenna::columnName(MSAntenna::MOUNT)), name_p(msAntenna, MSAntenna::columnName(MSAntenna::NAME)), offset_p(msAntenna, MSAntenna::columnName(MSAntenna::OFFSET)), position_p(msAntenna, MSAntenna::columnName(MSAntenna::POSITION)), station_p(msAntenna, MSAntenna::columnName(MSAntenna::STATION)), type_p(msAntenna, MSAntenna::columnName(MSAntenna::TYPE)), meanOrbit_p(), orbitId_p(), phasedArrayId_p(), offsetMeas_p(msAntenna, MSAntenna::columnName(MSAntenna::OFFSET)), positionMeas_p(msAntenna, MSAntenna::columnName(MSAntenna::POSITION)), dishDiameterQuant_p(msAntenna, MSAntenna:: columnName(MSAntenna::DISH_DIAMETER)), offsetQuant_p(msAntenna, MSAntenna::columnName(MSAntenna::OFFSET)), positionQuant_p(msAntenna, MSAntenna::columnName(MSAntenna::POSITION)) { attachOptionalCols(msAntenna); } ROMSAntennaColumns::~ROMSAntennaColumns() {} ROMSAntennaColumns::ROMSAntennaColumns(): dishDiameter_p(), flagRow_p(), mount_p(), name_p(), offset_p(), position_p(), station_p(), type_p(), meanOrbit_p(), orbitId_p(), phasedArrayId_p(), offsetMeas_p(), positionMeas_p(), dishDiameterQuant_p(), offsetQuant_p(), positionQuant_p() { } void ROMSAntennaColumns::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 ROMSAntennaColumns::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); } } Int ROMSAntennaColumns:: matchAntenna(const MPosition& antennaPos, const Quantum& tolerance, Int tryRow) { uInt 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("ROMSAntennaColumns::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 uInt tr = tryRow; if (tr >= r) { throw(AipsError("ROMSAntennaColumns::matchAntenna(...) - " "the row 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; } Int ROMSAntennaColumns::matchAntenna(const String& antName, const MPosition& antennaPos, const Quantum& tolerance, Int tryRow) { return matchAntennaAndStation(antName, "", antennaPos, tolerance, tryRow); } Int ROMSAntennaColumns::matchAntennaAndStation(const String& antName, const String& stationName, const MPosition& antennaPos, const Quantum& tolerance, Int tryRow) { uInt 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("ROMSAntennaColumns::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 uInt tr = tryRow; if (tr >= r) { throw(AipsError("ROMSAntennaColumns::matchAntenna(...) - " "the row 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 ROMSAntennaColumns::matchName(uInt row, const String& antName) const { DebugAssert(row < nrow(), AipsError); return antName.matches(name()(row)); } Bool ROMSAntennaColumns::matchStation(uInt row, const String& stationName) const { DebugAssert(row < nrow(), AipsError); return stationName.matches(station()(row)); } Bool ROMSAntennaColumns:: matchPosition(uInt row, const Vector& antPosInM, const Double tolInM) const { DebugAssert(row < nrow(), AipsError); DebugAssert(antPosInM.nelements() == 3, AipsError); return allNearAbs(position()(row), antPosInM, tolInM); } MSAntennaColumns::MSAntennaColumns(MSAntenna& msAntenna): ROMSAntennaColumns(msAntenna), dishDiameter_p(msAntenna, MSAntenna:: columnName(MSAntenna::DISH_DIAMETER)), flagRow_p(msAntenna, MSAntenna::columnName(MSAntenna::FLAG_ROW)), mount_p(msAntenna, MSAntenna::columnName(MSAntenna::MOUNT)), name_p(msAntenna, MSAntenna::columnName(MSAntenna::NAME)), offset_p(msAntenna, MSAntenna::columnName(MSAntenna::OFFSET)), position_p(msAntenna, MSAntenna::columnName(MSAntenna::POSITION)), station_p(msAntenna, MSAntenna::columnName(MSAntenna::STATION)), type_p(msAntenna, MSAntenna::columnName(MSAntenna::TYPE)), meanOrbit_p(), orbitId_p(), phasedArrayId_p(), offsetMeas_p(msAntenna, MSAntenna::columnName(MSAntenna::OFFSET)), positionMeas_p(msAntenna, MSAntenna::columnName(MSAntenna::POSITION)), dishDiameterQuant_p(msAntenna, MSAntenna:: columnName(MSAntenna::DISH_DIAMETER)), offsetQuant_p(msAntenna, MSAntenna::columnName(MSAntenna::OFFSET)), positionQuant_p(msAntenna, MSAntenna::columnName(MSAntenna::POSITION)) { 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); } MSAntennaColumns::~MSAntennaColumns() {} MSAntennaColumns::MSAntennaColumns(): ROMSAntennaColumns(), dishDiameter_p(), flagRow_p(), mount_p(), name_p(), offset_p(), position_p(), station_p(), type_p(), meanOrbit_p(), orbitId_p(), phasedArrayId_p(), offsetMeas_p(), positionMeas_p(), dishDiameterQuant_p(), offsetQuant_p(), positionQuant_p() { } void MSAntennaColumns::attach(MSAntenna& msAntenna) { ROMSAntennaColumns::attach(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(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); } // Local Variables: // compile-command: "gmake MSAntennaColumns" // End: } //# NAMESPACE CASACORE - END casacore-2.4.1/ms/MeasurementSets/MSAntennaColumns.h000066400000000000000000000323551321422335000224020ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 read-only access to MSAntenna columns // // // // // //
      • MSAntenna //
      • ArrayColumn //
      • ScalarColumn // // // // ROMSAntennaColumns stands for Read-Only MeasurementSet Antenna Table columns. // // // // This class provides read-only 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 ROMSColumns for an example. // // // // See MSColumns for the motivation. // class ROMSAntennaColumns { public: // Create a columns object that accesses the data in the specified Table ROMSAntennaColumns(const MSAntenna& msAntenna); // The destructor does nothing special ~ROMSAntennaColumns(); // Access to columns // const ROScalarColumn& dishDiameter() const {return dishDiameter_p;} const ROScalarQuantColumn& dishDiameterQuant() const {return dishDiameterQuant_p;} const ROScalarColumn& flagRow() const {return flagRow_p;} const ROScalarColumn& mount() const {return mount_p;} const ROScalarColumn& name() const {return name_p;} const ROArrayColumn& offset() const {return offset_p;} const ROArrayQuantColumn& offsetQuant() const {return offsetQuant_p;} const ROScalarMeasColumn& offsetMeas() const { return offsetMeas_p;} const ROArrayColumn& position() const {return position_p;} const ROArrayQuantColumn& positionQuant() const { return positionQuant_p;} const ROScalarMeasColumn& positionMeas() const { return positionMeas_p;} const ROScalarColumn& station() const {return station_p;} const ROScalarColumn& type() const {return type_p;} // // Access to optional columns // const ROArrayColumn& meanOrbit() const {return meanOrbit_p;} const ROScalarColumn& orbitId() const {return orbitId_p;} const ROScalarColumn& phasedArrayId() const {return phasedArrayId_p;} // // Convenience function that returns the number of rows in any of the columns uInt 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. Flaged // 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. Int matchAntenna(const MPosition& antennaPos, const Quantum& tolerance, Int tryRow=-1); // Same as the previous function except that the antenna name must also // match. Int matchAntenna(const String& antName, const MPosition& antennaPos, const Quantum& tolerance, Int tryRow=-1); // Same as the previous function except that the station name must also // match. Int matchAntennaAndStation(const String& antName, const String& stationName, // ignored when empty const MPosition& antennaPos, const Quantum& tolerance, Int tryRow=-1); protected: //# default constructor creates a object that is not usable. Use the attach //# function correct this. ROMSAntennaColumns(); //# 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. ROMSAntennaColumns(const ROMSAntennaColumns&); ROMSAntennaColumns& operator=(const ROMSAntennaColumns&); //# 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(uInt row, const String& antName) const; Bool matchStation(uInt row, const String& stationName) const; Bool matchPosition(uInt row, const Vector& antPosInM, const Double tolInM) const; //# required columns ROScalarColumn dishDiameter_p; ROScalarColumn flagRow_p; ROScalarColumn mount_p; ROScalarColumn name_p; ROArrayColumn offset_p; ROArrayColumn position_p; ROScalarColumn station_p; ROScalarColumn type_p; //# optional columns ROArrayColumn meanOrbit_p; ROScalarColumn orbitId_p; ROScalarColumn phasedArrayId_p; //# Access to Measure columns ROScalarMeasColumn offsetMeas_p; ROScalarMeasColumn positionMeas_p; //# Access to Quantum columns ROScalarQuantColumn dishDiameterQuant_p; ROArrayQuantColumn offsetQuant_p; ROArrayQuantColumn positionQuant_p; }; // // A class to provide easy read-write 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 ROMSAntennaColumns { public: // Create a columns object that accesses the data in the specified Table MSAntennaColumns(MSAntenna& msAntenna); // The destructor does nothing special ~MSAntennaColumns(); // Read-write 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;} // // Read-write access to optional columns // ArrayColumn& meanOrbit() {return meanOrbit_p;} ScalarColumn& orbitId() {return orbitId_p;} ScalarColumn& phasedArrayId() {return phasedArrayId_p;} // // Read-only access to required columns // const ROScalarColumn& dishDiameter() const { return ROMSAntennaColumns::dishDiameter();} const ROScalarQuantColumn& dishDiameterQuant() const { return ROMSAntennaColumns::dishDiameterQuant();} const ROScalarColumn& flagRow() const { return ROMSAntennaColumns::flagRow();} const ROScalarColumn& mount() const { return ROMSAntennaColumns::mount();} const ROScalarColumn& name() const { return ROMSAntennaColumns::name();} const ROArrayColumn& offset() const { return ROMSAntennaColumns::offset();} const ROArrayQuantColumn& offsetQuant() const { return ROMSAntennaColumns::offsetQuant();} const ROScalarMeasColumn& offsetMeas() const { return ROMSAntennaColumns::offsetMeas();} const ROArrayColumn& position() const { return ROMSAntennaColumns::position();} const ROArrayQuantColumn& positionQuant() const { return ROMSAntennaColumns::positionQuant();} const ROScalarMeasColumn& positionMeas() const { return ROMSAntennaColumns::positionMeas();} const ROScalarColumn& station() const { return ROMSAntennaColumns::station();} const ROScalarColumn& type() const { return ROMSAntennaColumns::type();} // // Read-only access to optional columns // const ROArrayColumn& meanOrbit() const { return ROMSAntennaColumns::meanOrbit();} const ROScalarColumn& orbitId() const { return ROMSAntennaColumns::orbitId();} const ROScalarColumn& phasedArrayId() const { return ROMSAntennaColumns::phasedArrayId();} // // 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); 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(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(MSAntenna& msAntenna); //# 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; }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/ms/MeasurementSets/MSAntennaEnums.h000066400000000000000000000077631321422335000220560ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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-2.4.1/ms/MeasurementSets/MSColumns.cc000066400000000000000000000077211321422335000212320ustar00rootroot00000000000000//# Mscolumnsc.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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Instantiate extern templates for often used types. #ifdef AIPS_CXX11 template class ArrayMeasColumn; template class ScalarMeasColumn; template class ScalarMeasColumn; template class ArrayQuantColumn; template class ScalarQuantColumn; #endif ROMSColumns::ROMSColumns(const MeasurementSet& ms): ROMSMainColumns(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()) { } ROMSColumns::~ROMSColumns() {} MSColumns::MSColumns(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); } } // Local Variables: // compile-command: "gmake MSColumns" // End: } //# NAMESPACE CASACORE - END casacore-2.4.1/ms/MeasurementSets/MSColumns.h000066400000000000000000000302141321422335000210650ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 read-only access to MeasurementSet columns // // // // // //
      • MeasurementSet //
      • ArrayColumn //
      • ScalarColumn // // // // ROMSColumns stands for Read-Only MeasurementSet Table columns. // // // // This class provides read-only 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. // // // // // // use as follows // MeasurementSet ms("myMS"); // ROMSColumns msc(ms); // // show data from row 5 // cout << msc.data()(5); // // show name of antenna on row 3 in antenna table // cout << msc.antenna().name(); // // // // // See MSColumns for the motivation. // // // //
      • We might decide to merge all the MSColumn classes with the // corresponding MeasurementSet classes. // class ROMSColumns: public ROMSMainColumns { public: // Create a columns object that accesses the data in the specified MS ROMSColumns(const MeasurementSet& ms); // The destructor does nothing special ~ROMSColumns(); // Access to required subtables // const ROMSAntennaColumns& antenna() const {return antenna_p;} const ROMSDataDescColumns& dataDescription() const {return dataDesc_p;} const ROMSFeedColumns& feed() const {return feed_p;} const ROMSFieldColumns& field() const {return field_p;} const ROMSFlagCmdColumns& flagCmd() const {return flagCmd_p;} const ROMSHistoryColumns& history() const {return history_p;} const ROMSObservationColumns& observation() const {return observation_p;} const ROMSPointingColumns& pointing() const {return pointing_p;} const ROMSPolarizationColumns& polarization() const { return polarization_p;} const ROMSProcessorColumns& processor() const {return processor_p;} const ROMSSpWindowColumns& spectralWindow() const { return spectralWindow_p;} const ROMSStateColumns& state() const {return state_p;} // // Access to optional subtables // const ROMSDopplerColumns& doppler() const {return doppler_p;} const ROMSFreqOffsetColumns& freqOffset() const {return freqOffset_p;} const ROMSSourceColumns& source() const {return source_p;} const ROMSSysCalColumns& sysCal() const {return sysCal_p;} const ROMSWeatherColumns& weather() const {return weather_p;} // private: // Access to subtables ROMSAntennaColumns antenna_p; ROMSDataDescColumns dataDesc_p; ROMSDopplerColumns doppler_p; //optional ROMSFeedColumns feed_p; ROMSFieldColumns field_p; ROMSFlagCmdColumns flagCmd_p; ROMSFreqOffsetColumns freqOffset_p; //optional ROMSHistoryColumns history_p; ROMSObservationColumns observation_p; ROMSPointingColumns pointing_p; ROMSPolarizationColumns polarization_p; ROMSProcessorColumns processor_p; ROMSSourceColumns source_p; // optional ROMSSpWindowColumns spectralWindow_p; ROMSStateColumns state_p; ROMSSysCalColumns sysCal_p; //optional ROMSWeatherColumns weather_p; //optional }; // // A class to provide easy read-write 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(MeasurementSet& ms); // The destructor does nothing special ~MSColumns(); // Read-write 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;} // // Read-write 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;} // // Read-only access to required subtables // const ROMSAntennaColumns& antenna() const {return antenna_p;} const ROMSDataDescColumns& dataDescription() const {return dataDesc_p;} const ROMSFeedColumns& feed() const {return feed_p;} const ROMSFieldColumns& field() const {return field_p;} const ROMSFlagCmdColumns& flagCmd() const {return flagCmd_p;} const ROMSHistoryColumns& history() const {return history_p;} const ROMSObservationColumns& observation() const {return observation_p;} const ROMSPointingColumns& pointing() const {return pointing_p;} const ROMSPolarizationColumns& polarization() const { return polarization_p;} const ROMSProcessorColumns& processor() const {return processor_p;} const ROMSSourceColumns& source() const {return source_p;} const ROMSSpWindowColumns& spectralWindow() const { return spectralWindow_p;} const ROMSStateColumns& state() const {return state_p;} // // Read-only access to optional subtables // const ROMSDopplerColumns& doppler() const {return doppler_p;} const ROMSFreqOffsetColumns& freqOffset() const {return freqOffset_p;} const ROMSSysCalColumns& sysCal() const {return sysCal_p;} const ROMSWeatherColumns& 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 }; //# Declare extern templates for often used types. #ifdef AIPS_CXX11 extern template class ArrayMeasColumn; extern template class ScalarMeasColumn; extern template class ScalarMeasColumn; extern template class ArrayQuantColumn; extern template class ScalarQuantColumn; #endif } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/ms/MeasurementSets/MSDataDescColumns.cc000066400000000000000000000121411321422335000226130ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN ROMSDataDescColumns::ROMSDataDescColumns() :flagRow_p(), polarizationId_p(), spectralWindowId_p(), lagId_p() { } ROMSDataDescColumns:: ROMSDataDescColumns(const MSDataDescription& msDataDesc): flagRow_p(msDataDesc, MSDataDescription:: columnName(MSDataDescription::FLAG_ROW)), polarizationId_p(msDataDesc, MSDataDescription:: columnName(MSDataDescription::POLARIZATION_ID)), spectralWindowId_p(msDataDesc, MSDataDescription:: columnName(MSDataDescription::SPECTRAL_WINDOW_ID)), lagId_p() { attachOptionalCols(msDataDesc); } ROMSDataDescColumns::~ROMSDataDescColumns() {} Int ROMSDataDescColumns::match(uInt spwId, uInt polId, Int tryRow) { uInt r = nrow(); if (r == 0) return -1; const Int spw = spwId; const Int pol = polId; // Main matching loop if (tryRow >= 0) { const uInt tr = tryRow; if (tr >= r) { throw(AipsError("ROMSDataDescColumns::match(...) - " "the row 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; } void ROMSDataDescColumns::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 ROMSDataDescColumns:: 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)); } } MSDataDescColumns::MSDataDescColumns(): ROMSDataDescColumns(), flagRow_p(), polarizationId_p(), spectralWindowId_p(), lagId_p() { } MSDataDescColumns::MSDataDescColumns(MSDataDescription& msDataDesc): ROMSDataDescColumns(msDataDesc), flagRow_p(msDataDesc, MSDataDescription:: columnName(MSDataDescription::FLAG_ROW)), polarizationId_p(msDataDesc, MSDataDescription:: columnName(MSDataDescription::POLARIZATION_ID)), spectralWindowId_p(msDataDesc,MSDataDescription:: columnName(MSDataDescription::SPECTRAL_WINDOW_ID)), lagId_p() { attachOptionalCols(msDataDesc); } MSDataDescColumns::~MSDataDescColumns() {} void MSDataDescColumns::attach(MSDataDescription& msDataDesc) { ROMSDataDescColumns::attach(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(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)); } } // Local Variables: // compile-command: "gmake MSDataDescColumns" // End: } //# NAMESPACE CASACORE - END casacore-2.4.1/ms/MeasurementSets/MSDataDescColumns.h000066400000000000000000000176351321422335000224720ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef MS_MSDATADESCCOLUMNS_H #define MS_MSDATADESCCOLUMNS_H #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN class MSDataDescription; // // A class to provide easy read-only access to MSDataDesc columns // // // // // //
      • MSDataDesc //
      • ScalarColumn // // // // ROMSDataDescColumns stands for Read-Only MeasurementSet DataDesc Table // columns. // // // // This class provides read-only 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 // ROMSColumns 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 ROMSDataDescColumns { public: // Create a columns object that accesses the data in the specified Table ROMSDataDescColumns(const MSDataDescription& msDataDesc); // The destructor does nothing special ~ROMSDataDescColumns(); // Access to required columns // const ROScalarColumn& flagRow() const {return flagRow_p;} const ROScalarColumn& polarizationId() const {return polarizationId_p;} const ROScalarColumn& spectralWindowId() const { return spectralWindowId_p;} // // Access to optional columns // const ROScalarColumn& lagId() const {return lagId_p;} // // Convenience function that returns the number of rows in any of the columns uInt 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). Int match(uInt spwId, uInt polId, Int tryRow=-1); protected: //# default constructor creates a object that is not usable. Use the attach //# function correct this. ROMSDataDescColumns(); //# attach this object to the supplied table. void attach(const MSDataDescription& msDataDesc); private: //# Make the assignment operator and the copy constructor private to prevent //# any compiler generated one from being used. ROMSDataDescColumns(const ROMSDataDescColumns&); ROMSDataDescColumns& operator=(const ROMSDataDescColumns&); //# Check if any optional columns exist and if so attach them. void attachOptionalCols(const MSDataDescription& msDataDesc); //# required columns ROScalarColumn flagRow_p; ROScalarColumn polarizationId_p; ROScalarColumn spectralWindowId_p; //# optional columns ROScalarColumn lagId_p; }; // // A class to provide easy read-write 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 ROMSDataDescColumns { public: // Create a columns object that accesses the data in the specified Table MSDataDescColumns(MSDataDescription& msDataDesc); // The destructor does nothing special ~MSDataDescColumns(); // Read-write access to required columns // ScalarColumn& flagRow() {return flagRow_p;} ScalarColumn& polarizationId() {return polarizationId_p;} ScalarColumn& spectralWindowId() {return spectralWindowId_p;} // // read-write access to optional columns // ScalarColumn& lagId() {return lagId_p;} // // Read-only access to required columns // const ROScalarColumn& flagRow() const { return ROMSDataDescColumns::flagRow();} const ROScalarColumn& polarizationId() const { return ROMSDataDescColumns::polarizationId();} const ROScalarColumn& spectralWindowId() const { return ROMSDataDescColumns::spectralWindowId();} // // Read-only access to optional columns // const ROScalarColumn& lagId() const { return ROMSDataDescColumns::lagId();} // 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(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(MSDataDescription& msDataDesc); //# required columns ScalarColumn flagRow_p; ScalarColumn polarizationId_p; ScalarColumn spectralWindowId_p; //# optional columns ScalarColumn lagId_p; }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/ms/MeasurementSets/MSDataDescEnums.h000066400000000000000000000063171321422335000221340ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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-2.4.1/ms/MeasurementSets/MSDataDescription.cc000066400000000000000000000134351321422335000226660ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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, uInt 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 &, uInt, 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; } void MSDataDescription::init() { if (! columnMap_p.ndefined()) { // the PredefinedColumns // FLAG_ROW colMapDef(FLAG_ROW,"FLAG_ROW", TpBool, "Flag this row","",""); // LAG_ID colMapDef(LAG_ID,"LAG_ID",TpInt,"The lag index","",""); // POLARIZATION_ID colMapDef(POLARIZATION_ID,"POLARIZATION_ID",TpInt, "Pointer to polarization table","",""); // SPECTRAL_WINDOW_ID colMapDef(SPECTRAL_WINDOW_ID, "SPECTRAL_WINDOW_ID", TpInt, "Pointer to spectralwindow table","",""); // PredefinedKeywords // init requiredTableDesc TableDesc requiredTD; // all required keywords uInt i; for (i = UNDEFINED_KEYWORD+1; i <= NUMBER_PREDEFINED_KEYWORDS; i++) { addKeyToDesc(requiredTD, PredefinedKeywords(i)); } // all required columns for (i = UNDEFINED_COLUMN+1; i <= NUMBER_REQUIRED_COLUMNS; i++) { addColumnToDesc(requiredTD, PredefinedColumns(i)); } requiredTD_p=new TableDesc(requiredTD); } } MSDataDescription MSDataDescription::referenceCopy(const String& newTableName, const Block& writableColumns) const { return MSDataDescription(MSTable:: referenceCopy(newTableName,writableColumns)); } } //# NAMESPACE CASACORE - END casacore-2.4.1/ms/MeasurementSets/MSDataDescription.h000066400000000000000000000121061321422335000225220ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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, uInt 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 void init(); private: // required by the need to throw an exception in the destructor Bool hasBeenDestroyed_p; }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/ms/MeasurementSets/MSDoppler.cc000066400000000000000000000143641321422335000212200ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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, uInt 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 &, uInt, 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; } void MSDoppler::init() { if (! columnMap_p.ndefined()) { // the PredefinedColumns // colMapDef(DOPPLER_ID,"DOPPLER_ID", TpInt, "Doppler tracking id","",""); // SOURCE_ID colMapDef(SOURCE_ID, "SOURCE_ID", TpInt, "Pointer to SOURCE table","",""); // TRANSITION_ID colMapDef(TRANSITION_ID,"TRANSITION_ID",TpInt, "Pointer to list of transitions in SOURCE table","",""); // VELDEF colMapDef(VELDEF, "VELDEF", TpDouble, "Velocity Definition for Doppler shift","m/s","Doppler"); // PredefinedKeywords // init requiredTableDesc TableDesc requiredTD; // all required keywords uInt i; for (i = UNDEFINED_KEYWORD+1; i <= NUMBER_PREDEFINED_KEYWORDS; i++) { addKeyToDesc(requiredTD, PredefinedKeywords(i)); } // all required columns for (i = UNDEFINED_COLUMN+1; i <= NUMBER_REQUIRED_COLUMNS; i++) { addColumnToDesc(requiredTD, PredefinedColumns(i)); } requiredTD_p=new TableDesc(requiredTD); } } MSDoppler MSDoppler::referenceCopy(const String& newTableName, const Block& writableColumns) const { return MSDoppler(MSTable:: referenceCopy(newTableName,writableColumns)); } } //# NAMESPACE CASACORE - END casacore-2.4.1/ms/MeasurementSets/MSDoppler.h000066400000000000000000000120171321422335000210530ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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, uInt 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 void init(); 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-2.4.1/ms/MeasurementSets/MSDopplerColumns.cc000066400000000000000000000072511321422335000225560ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN ROMSDopplerColumns::ROMSDopplerColumns(const MSDoppler& msDoppler): isNull_p(True), dopplerId_p(), sourceId_p(), transitionId_p(), velDef_p(), velDefMeas_p(), velDefQuant_p() { attach(msDoppler); } ROMSDopplerColumns::~ROMSDopplerColumns() {} ROMSDopplerColumns::ROMSDopplerColumns(): isNull_p(True), dopplerId_p(), sourceId_p(), transitionId_p(), velDef_p(), velDefMeas_p(), velDefQuant_p() { } void ROMSDopplerColumns::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)); } } MSDopplerColumns::MSDopplerColumns(MSDoppler& msDoppler): ROMSDopplerColumns(), dopplerId_p(), sourceId_p(), transitionId_p(), velDef_p(), velDefMeas_p(), velDefQuant_p() { attach(msDoppler); } MSDopplerColumns::~MSDopplerColumns() {} MSDopplerColumns::MSDopplerColumns(): ROMSDopplerColumns(), dopplerId_p(), sourceId_p(), transitionId_p(), velDef_p(), velDefMeas_p(), velDefQuant_p() { } void MSDopplerColumns::attach(MSDoppler& msDoppler) { ROMSDopplerColumns::attach(msDoppler); 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); } // Local Variables: // compile-command: "gmake MSDopplerColumns" // End: } //# NAMESPACE CASACORE - END casacore-2.4.1/ms/MeasurementSets/MSDopplerColumns.h000066400000000000000000000172251321422335000224220ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 read-only access to MSDoppler columns // // // // // //
      • MSDoppler //
      • ScalarColumn // // // // ROMSDopplerColumns stands for Read-Only MeasurementSet Doppler Table // columns. // // // // This class provides read-only 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 // ROMSColumns for an example. // // // // See MSColumns for the motivation. // class ROMSDopplerColumns { public: // Create a columns object that accesses the data in the specified Table ROMSDopplerColumns(const MSDoppler& msDoppler); // The destructor does nothing special ~ROMSDopplerColumns(); // Is this object defined? (MSDoppler table is optional) Bool isNull() const {return isNull_p;} // Access to columns // const ROScalarColumn& dopplerId() const {return dopplerId_p;} const ROScalarColumn& sourceId() const {return sourceId_p;} const ROScalarColumn& transitionId() const {return transitionId_p;} const ROScalarColumn& velDef() const {return velDef_p;} const ROScalarQuantColumn& velDefQuant() const {return velDefQuant_p;} const ROScalarMeasColumn& 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. uInt nrow() const {return isNull() ? 0 : dopplerId_p.nrow();} protected: //# default constructor creates a object that is not usable. Use the attach //# function correct this. ROMSDopplerColumns(); //# 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. ROMSDopplerColumns(const ROMSDopplerColumns&); ROMSDopplerColumns& operator=(const ROMSDopplerColumns&); //# Is the object not attached to a Table. Bool isNull_p; //# required columns ROScalarColumn dopplerId_p; ROScalarColumn sourceId_p; ROScalarColumn transitionId_p; ROScalarColumn velDef_p; //# Access to Measure columns ROScalarMeasColumn velDefMeas_p; //# Access to Quantum columns ROScalarQuantColumn velDefQuant_p; }; // // A class to provide easy read-write 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 ROMSDopplerColumns { public: // Create a columns object that accesses the data in the specified Table MSDopplerColumns(MSDoppler& msDoppler); // The destructor does nothing special ~MSDopplerColumns(); // Read-write 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;} // // Read-only access to required columns // const ROScalarColumn& dopplerId() const { return ROMSDopplerColumns::dopplerId();} const ROScalarColumn& sourceId() const { return ROMSDopplerColumns::sourceId();} const ROScalarColumn& transitionId() const { return ROMSDopplerColumns::transitionId();} const ROScalarColumn& velDef() const { return ROMSDopplerColumns::velDef();} const ROScalarQuantColumn& velDefQuant() const { return ROMSDopplerColumns::velDefQuant();} const ROScalarMeasColumn& velDefMeas() const { return ROMSDopplerColumns::velDefMeas();} // // 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(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&); //# 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; }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/ms/MeasurementSets/MSDopplerEnums.h000066400000000000000000000062321321422335000220650ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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-2.4.1/ms/MeasurementSets/MSDopplerUtil.cc000066400000000000000000000135131321422335000220510ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: //---------------------------------------------------------------------------- #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 ROMSColumns 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 (uInt 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 (uInt 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-2.4.1/ms/MeasurementSets/MSFeed.cc000066400000000000000000000161441321422335000204540ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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, uInt 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 &, uInt, 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; } void MSFeed::init() { if (! columnMap_p.ndefined()) { // the PredefinedColumns // ANTENNA_ID colMapDef(ANTENNA_ID, "ANTENNA_ID", TpInt, "ID of antenna in this array","",""); // BEAM_ID colMapDef(BEAM_ID,"BEAM_ID",TpInt, "Id for BEAM model","",""); // BEAM_OFFSET colMapDef(BEAM_OFFSET,"BEAM_OFFSET",TpArrayDouble, "Beam position offset (on sky but in antenna" "reference frame)","rad","Direction"); // FEED_ID colMapDef(FEED_ID,"FEED_ID",TpInt, "Feed id","",""); // FOCUS_LENGTH colMapDef(FOCUS_LENGTH,"FOCUS_LENGTH",TpDouble, "Focus length","m",""); // INTERVAL colMapDef(INTERVAL,"INTERVAL",TpDouble, "Interval for which this set of parameters is accurate", "s",""); // NUM_RECEPTORS colMapDef(NUM_RECEPTORS,"NUM_RECEPTORS",TpInt, "Number of receptors on this feed (probably 1 or 2)","",""); // PHASED_FEED_ID colMapDef(PHASED_FEED_ID,"PHASED_FEED_ID",TpInt, "index into PHASED_FEED table (ignore if<0)","",""); // POL_RESPONSE colMapDef(POL_RESPONSE,"POL_RESPONSE",TpArrayComplex, "D-matrix i.e. leakage between two receptors","",""); // POLARIZATION_TYPE colMapDef(POLARIZATION_TYPE,"POLARIZATION_TYPE",TpArrayString, "Type of polarization to which a given RECEPTOR responds", "",""); // POSITION colMapDef(POSITION,"POSITION",TpArrayDouble, "Position of feed relative to feed reference position", "m","Position"); // RECEPTOR_ANGLE colMapDef(RECEPTOR_ANGLE,"RECEPTOR_ANGLE",TpArrayDouble, "The reference angle for polarization","rad",""); // SPECTRAL_WINDOW_ID colMapDef(SPECTRAL_WINDOW_ID,"SPECTRAL_WINDOW_ID",TpInt, "ID for this spectral window setup","",""); // TIME colMapDef(TIME,"TIME",TpDouble, "Midpoint of time for which this set of " "parameters is accurate","s","Epoch"); // PredefinedKeywords // init requiredTableDesc TableDesc requiredTD; // all required keywords uInt i; for (i = UNDEFINED_KEYWORD+1; i <= NUMBER_PREDEFINED_KEYWORDS; i++) { addKeyToDesc(requiredTD, PredefinedKeywords(i)); } // all required columns // First define the columns with fixed size arrays IPosition shape(1,3); ColumnDesc::Option option=ColumnDesc::Direct; addColumnToDesc(requiredTD, POSITION, shape, option); // define the columns with known dimensionality addColumnToDesc(requiredTD, BEAM_OFFSET, 2); addColumnToDesc(requiredTD, POLARIZATION_TYPE, 1); addColumnToDesc(requiredTD, POL_RESPONSE, 2); addColumnToDesc(requiredTD, RECEPTOR_ANGLE, 1); // Now define all other columns (duplicates are skipped) for (i = UNDEFINED_COLUMN+1; i <= NUMBER_REQUIRED_COLUMNS; i++) { addColumnToDesc(requiredTD, PredefinedColumns(i)); } requiredTD_p=new TableDesc(requiredTD); } } MSFeed MSFeed::referenceCopy(const String& newTableName, const Block& writableColumns) const { return MSFeed(MSTable::referenceCopy (newTableName,writableColumns)); } } //# NAMESPACE CASACORE - END casacore-2.4.1/ms/MeasurementSets/MSFeed.h000066400000000000000000000114171321422335000203140ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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, uInt 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 void init(); private: // required by the need to throw an exception in the destructor Bool hasBeenDestroyed_p; }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/ms/MeasurementSets/MSFeedColumns.cc000066400000000000000000000323271321422335000220160ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN ROMSFeedColumns::ROMSFeedColumns(const MSFeed& msFeed): antennaId_p(msFeed, MSFeed::columnName(MSFeed::ANTENNA_ID)), beamId_p(msFeed, MSFeed::columnName(MSFeed::BEAM_ID)), beamOffset_p(msFeed, MSFeed::columnName(MSFeed::BEAM_OFFSET)), feedId_p(msFeed, MSFeed::columnName(MSFeed::FEED_ID)), interval_p(msFeed, MSFeed::columnName(MSFeed::INTERVAL)), numReceptors_p(msFeed, MSFeed::columnName(MSFeed::NUM_RECEPTORS)), polResponse_p(msFeed, MSFeed::columnName(MSFeed::POL_RESPONSE)), polarizationType_p(msFeed, MSFeed:: columnName(MSFeed::POLARIZATION_TYPE)), position_p(msFeed, MSFeed::columnName(MSFeed::POSITION)), receptorAngle_p(msFeed, MSFeed::columnName(MSFeed::RECEPTOR_ANGLE)), spectralWindowId_p(msFeed, MSFeed:: columnName(MSFeed::SPECTRAL_WINDOW_ID)), time_p(msFeed, MSFeed::columnName(MSFeed::TIME)), focusLength_p(), phasedFeedId_p(), beamOffsetMeas_p(msFeed, MSFeed::columnName(MSFeed::BEAM_OFFSET)), positionMeas_p(msFeed, MSFeed::columnName(MSFeed::POSITION)), timeMeas_p(msFeed, MSFeed::columnName(MSFeed::TIME)), beamOffsetQuant_p(msFeed, MSFeed::columnName(MSFeed::BEAM_OFFSET)), intervalQuant_p(msFeed, MSFeed::columnName(MSFeed::INTERVAL)), positionQuant_p(msFeed, MSFeed::columnName(MSFeed::POSITION)), receptorAngleQuant_p(msFeed, MSFeed:: columnName(MSFeed::RECEPTOR_ANGLE)), timeQuant_p(msFeed, MSFeed::columnName(MSFeed::TIME)), focusLengthQuant_p() { attachOptionalCols(msFeed); } ROMSFeedColumns::~ROMSFeedColumns() {} ROMSFeedColumns::ROMSFeedColumns(): antennaId_p(), beamId_p(), beamOffset_p(), feedId_p(), interval_p(), numReceptors_p(), polResponse_p(), polarizationType_p(), position_p(), receptorAngle_p(), spectralWindowId_p(), time_p(), focusLength_p(), phasedFeedId_p(), beamOffsetMeas_p(), positionMeas_p(), timeMeas_p(), beamOffsetQuant_p(), intervalQuant_p(), positionQuant_p(), receptorAngleQuant_p(), timeQuant_p(), focusLengthQuant_p() { } void ROMSFeedColumns::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 ROMSFeedColumns::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); } Int ROMSFeedColumns::matchFeed(Quantum& newTimeQ, Quantum& newIntervalQ, const Int& antId, const Int& fId, const Int& spwId, const Quantum& timeQ, const Quantum& intervalQ, const Int& numRec, const Array >& beamOffsetQ, const Array& polType, const Array& polResp, const Array >& positionQ, const Array >& receptorAngleQ, const Vector& ignoreRows, const Quantum& focusLengthQ ){ const Unit d("deg"); const Unit s("s"); const Unit m("m"); newTimeQ = newIntervalQ = Quantum(0.,s); uInt 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(uInt i=0; i= timeInS+halfIntervalInS) ){ // only difference is the validity time newTimeQ = (timeQuant()(r)+timeQ)/2.; Double maxTime = max(timeQuant()(r).getValue(s)+modHalfIntervalInS, timeInS+halfIntervalInS); newIntervalQ = Quantum(2*(maxTime-newTimeQ.getValue(s)), s); } return r; } } } } return -1; } MSFeedColumns::MSFeedColumns(MSFeed& msFeed): ROMSFeedColumns(msFeed), antennaId_p(msFeed, MSFeed::columnName(MSFeed::ANTENNA_ID)), beamId_p(msFeed, MSFeed::columnName(MSFeed::BEAM_ID)), beamOffset_p(msFeed, MSFeed::columnName(MSFeed::BEAM_OFFSET)), feedId_p(msFeed, MSFeed::columnName(MSFeed::FEED_ID)), interval_p(msFeed, MSFeed::columnName(MSFeed::INTERVAL)), numReceptors_p(msFeed, MSFeed::columnName(MSFeed::NUM_RECEPTORS)), polResponse_p(msFeed, MSFeed::columnName(MSFeed::POL_RESPONSE)), polarizationType_p(msFeed, MSFeed:: columnName(MSFeed::POLARIZATION_TYPE)), position_p(msFeed, MSFeed::columnName(MSFeed::POSITION)), receptorAngle_p(msFeed, MSFeed::columnName(MSFeed::RECEPTOR_ANGLE)), spectralWindowId_p(msFeed, MSFeed:: columnName(MSFeed::SPECTRAL_WINDOW_ID)), time_p(msFeed, MSFeed::columnName(MSFeed::TIME)), focusLength_p(), phasedFeedId_p(), beamOffsetMeas_p(msFeed, MSFeed::columnName(MSFeed::BEAM_OFFSET)), positionMeas_p(msFeed, MSFeed::columnName(MSFeed::POSITION)), timeMeas_p(msFeed, MSFeed::columnName(MSFeed::TIME)), beamOffsetQuant_p(msFeed, MSFeed::columnName(MSFeed::BEAM_OFFSET)), intervalQuant_p(msFeed, MSFeed::columnName(MSFeed::INTERVAL)), positionQuant_p(msFeed, MSFeed::columnName(MSFeed::POSITION)), receptorAngleQuant_p(msFeed, MSFeed:: columnName(MSFeed::RECEPTOR_ANGLE)), timeQuant_p(msFeed, MSFeed::columnName(MSFeed::TIME)), focusLengthQuant_p() { attachOptionalCols(msFeed); } MSFeedColumns::~MSFeedColumns() {} 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); } MSFeedColumns::MSFeedColumns(): ROMSFeedColumns(), antennaId_p(), beamId_p(), beamOffset_p(), feedId_p(), interval_p(), numReceptors_p(), polResponse_p(), polarizationType_p(), position_p(), receptorAngle_p(), spectralWindowId_p(), time_p(), focusLength_p(), phasedFeedId_p(), beamOffsetMeas_p(), positionMeas_p(), timeMeas_p(), beamOffsetQuant_p(), intervalQuant_p(), positionQuant_p(), receptorAngleQuant_p(), timeQuant_p(), focusLengthQuant_p() { } void MSFeedColumns::attach(MSFeed& msFeed) { ROMSFeedColumns::attach(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(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); } // Local Variables: // compile-command: "gmake MSFeedColumns" // End: } //# NAMESPACE CASACORE - END casacore-2.4.1/ms/MeasurementSets/MSFeedColumns.h000066400000000000000000000374161321422335000216640ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 read-only access to MSFeed columns // // // // // //
      • MSFeed //
      • ArrayColumn //
      • ScalarColumn // // // // ROMSFeedColumns stands for Read-Only MeasurementSet Feed Table columns. // // // // This class provides read-only 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 ROMSColumns for an example. // // // // See MSColumns for the motivation. // class ROMSFeedColumns { public: // Create a columns object that accesses the data in the specified Table ROMSFeedColumns(const MSFeed& msFeed); // The destructor does nothing special ~ROMSFeedColumns(); // Access to required columns // const ROScalarColumn& antennaId() const {return antennaId_p;} const ROScalarColumn& beamId() const {return beamId_p;} const ROArrayColumn& beamOffset() const {return beamOffset_p;} const ROArrayQuantColumn& beamOffsetQuant() const { return beamOffsetQuant_p;} const ROArrayMeasColumn& beamOffsetMeas() const {return beamOffsetMeas_p;} const ROScalarColumn& feedId() const {return feedId_p;} const ROScalarColumn& interval() const {return interval_p;} const ROScalarQuantColumn& intervalQuant() const { return intervalQuant_p;} const ROScalarColumn& numReceptors() const {return numReceptors_p;} const ROArrayColumn& polResponse() const {return polResponse_p;} const ROArrayColumn& polarizationType() const { return polarizationType_p;} const ROArrayColumn& position() const {return position_p;} const ROArrayQuantColumn& positionQuant() const { return positionQuant_p;} const ROScalarMeasColumn& positionMeas() const { return positionMeas_p;} const ROArrayColumn& receptorAngle() const {return receptorAngle_p;} const ROArrayQuantColumn& receptorAngleQuant() const { return receptorAngleQuant_p;} const ROScalarColumn& spectralWindowId() const { return spectralWindowId_p;} const ROScalarColumn& time() const {return time_p;} const ROScalarQuantColumn& timeQuant() const { return timeQuant_p;} const ROScalarMeasColumn& timeMeas() const {return timeMeas_p;} // // Access to optional columns // const ROScalarColumn& focusLength() const {return focusLength_p;} const ROScalarQuantColumn& focusLengthQuant() const { return focusLengthQuant_p;} const ROScalarColumn& phasedFeedId() const {return phasedFeedId_p;} // // Convenience function that returns the number of rows in any of the columns uInt 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. Int matchFeed(Quantum& newTimeQ, Quantum& newIntervalQ, const Int& antId, const Int& fId, // feedId const Int& spwId, const Quantum& timeQ, const Quantum& intervalQ, const Int& numRec, const Array >& beamOffsetQ, const Array& polType, const Array& polResp, const Array >& positionQ, const Array >& receptorAngleQ, const Vector& ignoreRows, const Quantum& focusLengthQ=Quantum() ); protected: //# default constructor creates a object that is not usable. Use the attach //# function correct this. ROMSFeedColumns(); //# 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. ROMSFeedColumns(const ROMSFeedColumns&); ROMSFeedColumns& operator=(const ROMSFeedColumns&); //# Check if any optional columns exist and if so attach them. void attachOptionalCols(const MSFeed& msFeed); //# required columns ROScalarColumn antennaId_p; ROScalarColumn beamId_p; ROArrayColumn beamOffset_p; ROScalarColumn feedId_p; ROScalarColumn interval_p; ROScalarColumn numReceptors_p; ROArrayColumn polResponse_p; ROArrayColumn polarizationType_p; ROArrayColumn position_p; ROArrayColumn receptorAngle_p; ROScalarColumn spectralWindowId_p; ROScalarColumn time_p; //# optional columns ROScalarColumn focusLength_p; ROScalarColumn phasedFeedId_p; // Access to Measure columns ROArrayMeasColumn beamOffsetMeas_p; ROScalarMeasColumn positionMeas_p; ROScalarMeasColumn timeMeas_p; // Access to Quantum columns ROArrayQuantColumn beamOffsetQuant_p; ROScalarQuantColumn intervalQuant_p; ROArrayQuantColumn positionQuant_p; ROArrayQuantColumn receptorAngleQuant_p; ROScalarQuantColumn timeQuant_p; //# optional Quantum columns ROScalarQuantColumn focusLengthQuant_p; }; // // A class to provide easy read-write 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 ROMSFeedColumns { public: // Create a columns object that accesses the data in the specified Table MSFeedColumns(MSFeed& msFeed); // The desctructor does nothing special ~MSFeedColumns(); // Read-write 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;} // // Read-write access to optional columns // ScalarColumn& focusLength() {return focusLength_p;} ScalarQuantColumn& focusLengthQuant() { return focusLengthQuant_p;} ScalarColumn& phasedFeedId() {return phasedFeedId_p;} // // Read-only access to required columns // const ROScalarColumn& antennaId() const { return ROMSFeedColumns::antennaId();} const ROScalarColumn& beamId() const { return ROMSFeedColumns::beamId();} const ROArrayColumn& beamOffset() const { return ROMSFeedColumns::beamOffset();} const ROArrayQuantColumn& beamOffsetQuant() const { return ROMSFeedColumns::beamOffsetQuant();} const ROArrayMeasColumn& beamOffsetMeas() const { return ROMSFeedColumns::beamOffsetMeas();} const ROScalarColumn& feedId() const { return ROMSFeedColumns::feedId();} const ROScalarColumn& interval() const { return ROMSFeedColumns::interval();} const ROScalarQuantColumn& intervalQuant() const { return ROMSFeedColumns::intervalQuant();} const ROScalarColumn& numReceptors() const { return ROMSFeedColumns::numReceptors();} const ROArrayColumn& polResponse() const { return ROMSFeedColumns::polResponse();} const ROArrayColumn& polarizationType() const { return ROMSFeedColumns::polarizationType();} const ROArrayColumn& position() const { return ROMSFeedColumns::position();} const ROArrayQuantColumn& positionQuant() const { return ROMSFeedColumns::positionQuant();} const ROScalarMeasColumn& positionMeas() const { return ROMSFeedColumns::positionMeas();} const ROArrayColumn& receptorAngle() const { return ROMSFeedColumns::receptorAngle();} const ROArrayQuantColumn& receptorAngleQuant() const { return ROMSFeedColumns::receptorAngleQuant();} const ROScalarColumn& spectralWindowId() const { return ROMSFeedColumns::spectralWindowId();} const ROScalarColumn& time() const { return ROMSFeedColumns::time();} const ROScalarQuantColumn& timeQuant() const { return ROMSFeedColumns::timeQuant();} const ROScalarMeasColumn& timeMeas() const { return ROMSFeedColumns::timeMeas();} // // Read-only access to optional columns // const ROScalarColumn& focusLength() const { return ROMSFeedColumns::focusLength();} const ROScalarQuantColumn& focusLengthQuant() const { return ROMSFeedColumns::focusLengthQuant();} const ROScalarColumn& phasedFeedId() const { return ROMSFeedColumns::phasedFeedId();} // // 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); 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(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(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; }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/ms/MeasurementSets/MSFeedEnums.h000066400000000000000000000104651321422335000213260ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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-2.4.1/ms/MeasurementSets/MSField.cc000066400000000000000000000204101321422335000206230ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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, uInt 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 &, uInt, 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; } void MSField::init() { if (! columnMap_p.ndefined()) { // the PredefinedColumns // CODE colMapDef(CODE, "CODE", TpString, "Special characteristics of field, " "e.g. Bandpass calibrator","",""); // DELAY_DIR colMapDef(DELAY_DIR, "DELAY_DIR", TpArrayDouble, "Direction of delay center (e.g. RA, DEC)" "as polynomial in time.","rad","Direction"); // EPHEMERIS_ID colMapDef(EPHEMERIS_ID,"EPHEMERIS_ID", TpInt, "Ephemeris id, pointer to EPHEMERIS table","",""); // FLAG_ROW colMapDef(FLAG_ROW, "FLAG_ROW", TpBool, "Row Flag","",""); // NAME colMapDef(NAME, "NAME", TpString, "Name of this field","",""); // NUM_POLY colMapDef(NUM_POLY, "NUM_POLY", TpInt, "Polynomial order of _DIR columns","",""); // PHASE_DIR colMapDef(PHASE_DIR, "PHASE_DIR", TpArrayDouble, "Direction of phase center (e.g. RA, DEC).", "rad","Direction"); // REFERENCE_DIR colMapDef(REFERENCE_DIR, "REFERENCE_DIR", TpArrayDouble, "Direction of REFERENCE center (e.g. RA, DEC)." "as polynomial in time.","rad","Direction"); // SOURCE_ID colMapDef(SOURCE_ID, "SOURCE_ID", TpInt, "Source id","",""); // TIME colMapDef(TIME, "TIME", TpDouble, "Time origin for direction and rate","s","Epoch"); // PredefinedKeywords // init requiredTableDesc TableDesc requiredTD; // all required keywords uInt i; for (i = UNDEFINED_KEYWORD+1; i <= NUMBER_PREDEFINED_KEYWORDS; i++) { addKeyToDesc(requiredTD, PredefinedKeywords(i)); } // all required columns // First define the columns with known dimensionality addColumnToDesc(requiredTD, DELAY_DIR, 2); addColumnToDesc(requiredTD, PHASE_DIR, 2); addColumnToDesc(requiredTD, REFERENCE_DIR, 2); // Now define all other columns (duplicates are skipped) for (i = UNDEFINED_COLUMN+1; i <= NUMBER_REQUIRED_COLUMNS; i++) { addColumnToDesc(requiredTD, PredefinedColumns(i)); } requiredTD_p=new TableDesc(requiredTD); } } 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(uInt 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, uInt 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 void init(); private: // required by the need to throw an exception in the destructor Bool hasBeenDestroyed_p; }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/ms/MeasurementSets/MSFieldColumns.cc000066400000000000000000000461121321422335000221730ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN ROMSFieldColumns::ROMSFieldColumns(const MSField& msField): measCometsPath_p(), measCometsV_p(), ephIdToMeasComet_p(-1), name_p(msField, MSField::columnName(MSField::NAME)), code_p(msField, MSField::columnName(MSField::CODE)), time_p(msField, MSField::columnName(MSField::TIME)), numPoly_p(msField, MSField::columnName(MSField::NUM_POLY)), delayDir_p(msField, MSField::columnName(MSField::DELAY_DIR)), phaseDir_p(msField, MSField::columnName(MSField::PHASE_DIR)), referenceDir_p(msField, MSField::columnName(MSField::REFERENCE_DIR)), sourceId_p(msField, MSField::columnName(MSField::SOURCE_ID)), flagRow_p(msField, MSField::columnName(MSField::FLAG_ROW)), ephemerisId_p(), timeMeas_p(msField, MSField::columnName(MSField::TIME)), delayDirMeas_p(msField, MSField::columnName(MSField::DELAY_DIR)), phaseDirMeas_p(msField, MSField::columnName(MSField::PHASE_DIR)), referenceDirMeas_p(msField, MSField::columnName(MSField::REFERENCE_DIR)), timeQuant_p(msField, MSField::columnName(MSField::TIME)) { attachOptionalCols(msField); } ROMSFieldColumns::~ROMSFieldColumns() { // EPHEM for(uInt i=0; i0){ return MSFieldColumns::interpolateDirMeas(delayDirMeasCol()(row), npoly, interTime, time()(row)); } else{ Vector vecDir(delayDirMeasCol()(row)); return extractDirMeas(vecDir(0), measCometIndex(row), interTime, timeMeas()(row)); } } MDirection ROMSFieldColumns::phaseDirMeas(Int 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 ROMSFieldColumns::referenceDirMeas(Int 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)); } } MRadialVelocity ROMSFieldColumns::radVelMeas(Int 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 << "ROMSFieldColumns::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 ROMSFieldColumns::rho(Int 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 << "ROMSFieldColumns::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 ROMSFieldColumns::needInterTime(Int row) const { if( ( measCometsV_p.size()>0 && ephemerisId()(row)>=0 ) || (numPoly()(row)>0) ){ return True; } return False; } Int ROMSFieldColumns::measCometIndex(Int row) const { Int rval = -1; if( measCometsV_p.size()>0 ){ Int ephId = ephemerisId()(row); if(ephId>=0 && ephIdToMeasComet_p.isDefined(ephId)){ rval = ephIdToMeasComet_p(ephId); } } return rval; } String ROMSFieldColumns::ephemPath(Int row) const { String rval = ""; Int index = measCometIndex(row); if( index>=0 ){ rval = measCometsV_p(index)->getTablePath(); } return rval; } Bool ROMSFieldColumns:: matchReferenceDir(uInt row, const MVDirection& dirVal, const Double& sepInRad, MVDirection& mvdir, Double time) const { try{ mvdir = referenceDirMeas(row, time).getAngle(); } catch(AipsError x){ return False; } if (dirVal.separation(mvdir) < sepInRad) { return True; } else { return False; } } Bool ROMSFieldColumns:: matchDelayDir(uInt row, const MVDirection& dirVal, const Double& sepInRad, MVDirection& mvdir, Double time) const { try{ mvdir = delayDirMeas(row, time).getAngle(); } catch(AipsError x){ return False; } if (dirVal.separation(mvdir) < sepInRad) { return True; } else { return False; } } Bool ROMSFieldColumns:: matchPhaseDir(uInt row, const MVDirection& dirVal, const Double& sepInRad, MVDirection& mvdir, Double time) const { try{ mvdir = phaseDirMeas(row, time).getAngle(); } catch(AipsError x){ return False; } if (dirVal.separation(mvdir) < sepInRad) { return True; } else { return False; } } Int ROMSFieldColumns::matchDirection(const MDirection& referenceDirection, const MDirection& delayDirection, const MDirection& phaseDirection, const Quantum& maxSeparation, Int tryRow, Double time) { uInt 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 uInt tr = tryRow; if (tr >= r) { throw(AipsError("ROMSFieldColumns::matchDirection(...) - " "the row 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(uInt i=0; i=0 && !ephIdToMeasComet_p.isDefined(theEphId)){ // 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); uInt 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.define(theEphId, nMeasCom); //cout << "Found and successfully opened ephemeris table " << ephemTablePath << endl; } } } MDirection ROMSFieldColumns::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 << "ROMSFieldColumns::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 ROMSFieldColumns::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; } } MSFieldColumns::MSFieldColumns(MSField& msField): ROMSFieldColumns(msField), name_p(msField,MSField::columnName(MSField::NAME)), code_p(msField,MSField::columnName(MSField::CODE)), time_p(msField,MSField::columnName(MSField::TIME)), numPoly_p(msField,MSField::columnName(MSField::NUM_POLY)), delayDir_p(msField,MSField::columnName(MSField::DELAY_DIR)), phaseDir_p(msField,MSField::columnName(MSField::PHASE_DIR)), referenceDir_p(msField,MSField::columnName(MSField::REFERENCE_DIR)), sourceId_p(msField,MSField::columnName(MSField::SOURCE_ID)), flagRow_p(msField,MSField::columnName(MSField::FLAG_ROW)), ephemerisId_p(), timeMeas_p(msField,MSField::columnName(MSField::TIME)), delayDirMeas_p(msField,MSField::columnName(MSField::DELAY_DIR)), phaseDirMeas_p(msField,MSField::columnName(MSField::PHASE_DIR)), referenceDirMeas_p(msField, MSField::columnName(MSField::REFERENCE_DIR)), timeQuant_p(msField,MSField::columnName(MSField::TIME)) { attachOptionalCols(msField); } MSFieldColumns::~MSFieldColumns() {} MSFieldColumns::MSFieldColumns(): ROMSFieldColumns(), name_p(), code_p(), time_p(), numPoly_p(), delayDir_p(), phaseDir_p(), referenceDir_p(), sourceId_p(), flagRow_p(), ephemerisId_p(), timeMeas_p(), delayDirMeas_p(), phaseDirMeas_p(), referenceDirMeas_p(), timeQuant_p() { } void MSFieldColumns::attach(MSField& msField) { ROMSFieldColumns::attach(msField); name_p.attach(msField, MSField::columnName(MSField::NAME)); code_p.attach(msField, MSField::columnName(MSField::CODE)); time_p.attach(msField, MSField::columnName(MSField::TIME)); numPoly_p.attach(msField, MSField::columnName(MSField::NUM_POLY)); delayDir_p.attach(msField, MSField::columnName(MSField::DELAY_DIR)); phaseDir_p.attach(msField, MSField::columnName(MSField::PHASE_DIR)); referenceDir_p.attach(msField, MSField::columnName(MSField::REFERENCE_DIR)); sourceId_p.attach(msField, MSField::columnName(MSField::SOURCE_ID)); flagRow_p.attach(msField, MSField::columnName(MSField::FLAG_ROW)); timeMeas_p.attach(msField, MSField::columnName(MSField::TIME)); delayDirMeas_p.attach(msField,MSField::columnName(MSField::DELAY_DIR)); phaseDirMeas_p.attach(msField,MSField::columnName(MSField::PHASE_DIR)); referenceDirMeas_p.attach(msField, MSField::columnName(MSField::REFERENCE_DIR)); timeQuant_p.attach(msField, MSField::columnName(MSField::TIME)); attachOptionalCols(msField); } void MSFieldColumns::attachOptionalCols(MSField& msField) { const ColumnDescSet& cds = msField.tableDesc().columnDescSet(); const String& ephemerisId = MSField::columnName(MSField::EPHEMERIS_ID); if (cds.isDefined(ephemerisId)){ ephemerisId_p.attach(msField, ephemerisId); measCometsPath_p = Path(msField.tableName()).absoluteName(); updateMeasComets(); } } MDirection MSFieldColumns:: interpolateDirMeas(const Array& 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); } // Local Variables: // compile-command: "gmake MSFieldColumns" // End: } //# NAMESPACE CASACORE - END casacore-2.4.1/ms/MeasurementSets/MSFieldColumns.h000066400000000000000000000375171321422335000220460ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef MS_MSFIELDCOLUMNS_H #define MS_MSFIELDCOLUMNS_H #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; template class Matrix; // // A class to provide easy access to MSField columns // // // // // //
      • MSField //
      • ArrayColumn //
      • ScalarColumn // // // // ROMSFieldColumns stands for Read-Only MeasurementSet Field Table columns. // // // // This class provides read-only 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 ROMSColumns for an example. // // // // See MSColumns for the motivation. // class ROMSFieldColumns { public: // Construct from the supplied Table ROMSFieldColumns(const MSField& msField); // The desctructor does nothing special ~ROMSFieldColumns(); // Read-only access to required columns // const ROScalarColumn& code() const {return code_p;} const ROArrayColumn& delayDir() const {return delayDir_p;} const ROArrayMeasColumn& delayDirMeasCol() const {return delayDirMeas_p;} const ROScalarColumn& flagRow() const {return flagRow_p;} const ROScalarColumn& name() const {return name_p;} const ROScalarColumn& numPoly() const {return numPoly_p;} const ROArrayColumn& phaseDir() const {return phaseDir_p;} const ROArrayMeasColumn& phaseDirMeasCol() const {return phaseDirMeas_p;} const ROArrayColumn& referenceDir() const {return referenceDir_p;} const ROArrayMeasColumn& referenceDirMeasCol() const {return referenceDirMeas_p;} const ROScalarColumn& sourceId() const {return sourceId_p;} const ROScalarColumn& time() const {return time_p;} const ROScalarQuantColumn& timeQuant() const { return timeQuant_p;} const ROScalarMeasColumn& timeMeas() const { return timeMeas_p;} // // Read-only access to optional columns // const ROScalarColumn& ephemerisId() const {return ephemerisId_p;} // // 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. // // 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(Int row, Double time = 0) const; MDirection phaseDirMeas(Int row, Double time = 0) const; MDirection referenceDirMeas(Int row, Double time = 0) const; MRadialVelocity radVelMeas(Int row, Double time = 0) const; Quantity rho(Int row, Double time = 0) const; Bool needInterTime(Int row) const; String ephemPath(Int row) const; // // Convenience function that returns the number of rows in any of the columns uInt 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. Int matchDirection(const MDirection& referenceDirection, const MDirection& delayDirection, const MDirection& phaseDirection, const Quantum& maxSeparation, Int 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. ROMSFieldColumns(); //# attach this object to the supplied table. void attach(const MSField& msField); Int measCometIndex(int row) const; String measCometsPath_p; Vector measCometsV_p; SimpleOrderedMap ephIdToMeasComet_p; // 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; private: //# Make the assignment operator and the copy constructor private to prevent //# any compiler generated one from being used. ROMSFieldColumns(const ROMSFieldColumns&); ROMSFieldColumns& operator=(const ROMSFieldColumns&); //# 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(uInt row, const MVDirection& dirVal, const Double& sepInRad, MVDirection& mvdir, Double time=0) const; Bool matchDelayDir(uInt row, const MVDirection& dirVal, const Double& sepInRad, MVDirection& mvdir, Double time=0) const; Bool matchPhaseDir(uInt row, const MVDirection& dirVal, const Double& sepInRad, MVDirection& mvdir, Double time=0) const; // //# required columns ROScalarColumn name_p; ROScalarColumn code_p; ROScalarColumn time_p; ROScalarColumn numPoly_p; ROArrayColumn delayDir_p; ROArrayColumn phaseDir_p; ROArrayColumn referenceDir_p; ROScalarColumn sourceId_p; ROScalarColumn flagRow_p; //# optional columns ROScalarColumn ephemerisId_p; //# Access to Measure columns ROScalarMeasColumn timeMeas_p; ROArrayMeasColumn delayDirMeas_p; ROArrayMeasColumn phaseDirMeas_p; ROArrayMeasColumn referenceDirMeas_p; //# Access to Quantum columns ROScalarQuantColumn timeQuant_p; }; // // A class to provide easy read-write 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 ROMSFieldColumns { public: // Construct from the supplied Table MSFieldColumns(MSField& msField); // The desctructor does nothing special ~MSFieldColumns(); // Read-write 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. These functions are in the ROMSFieldColumns class. // 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;} // // Read-write access to optional columns // ScalarColumn& ephemerisId() {return ephemerisId_p;} // // Read-only access to required columns // const ROScalarColumn& code() const { return ROMSFieldColumns::code();} const ROArrayColumn& delayDir() const { return ROMSFieldColumns::delayDir();} const ROArrayMeasColumn& delayDirMeasCol() const { return ROMSFieldColumns::delayDirMeasCol();} const ROScalarColumn& flagRow() const { return ROMSFieldColumns::flagRow();} const ROScalarColumn& name() const { return ROMSFieldColumns::name();} const ROScalarColumn& numPoly() const { return ROMSFieldColumns::numPoly();} const ROArrayColumn& phaseDir() const { return ROMSFieldColumns::phaseDir();} const ROArrayMeasColumn& phaseDirMeasCol() const { return ROMSFieldColumns::phaseDirMeasCol();} const ROArrayColumn& referenceDir() const { return ROMSFieldColumns::referenceDir();} const ROArrayMeasColumn& referenceDirMeasCol() const { return ROMSFieldColumns::referenceDirMeasCol();} const ROScalarColumn& sourceId() const { return ROMSFieldColumns::sourceId();} const ROScalarColumn& time() const { return ROMSFieldColumns::time();} const ROScalarQuantColumn& timeQuant() const { return ROMSFieldColumns::timeQuant();} const ROScalarMeasColumn& timeMeas() const { return ROMSFieldColumns::timeMeas();} // // Read-only access to optional columns // const ROScalarColumn& ephemerisId() const { return ROMSFieldColumns::ephemerisId();} // // 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); 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(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(MSField& msField); //# 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; }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/ms/MeasurementSets/MSFieldEnums.h000066400000000000000000000076101321422335000215040ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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-2.4.1/ms/MeasurementSets/MSFlagCmd.cc000066400000000000000000000136001321422335000211000ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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, uInt 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; } void MSFlagCmd::init() { if (! columnMap_p.ndefined()) { // the PredefinedColumns // APPLIED colMapDef(APPLIED, "APPLIED", TpBool, "True if flag has been applied to main table","",""); // COMMAND colMapDef(COMMAND, "COMMAND", TpString, "Flagging command","",""); // INTERVAL colMapDef(INTERVAL,"INTERVAL", TpDouble, "Time interval for which this flag is valid","s",""); // LEVEL colMapDef(LEVEL, "LEVEL", TpInt, "Flag level - revision level ","",""); // REASON colMapDef(REASON, "REASON", TpString, "Flag reason","",""); // SEVERITY colMapDef(SEVERITY, "SEVERITY", TpInt, "Severity code (0-10) ","",""); // TIME colMapDef(TIME, "TIME", TpDouble, "Midpoint of interval for which this flag is valid", "s","Epoch"); // TYPE colMapDef(TYPE, "TYPE", TpString, "Type of flag (FLAG or UNFLAG)","",""); // PredefinedKeywords // init requiredTableDesc TableDesc requiredTD; // all required keywords uInt i; for (i = UNDEFINED_KEYWORD+1; i <= NUMBER_PREDEFINED_KEYWORDS; i++) { addKeyToDesc(requiredTD, PredefinedKeywords(i)); } // all required columns // Now define all other columns (duplicates are skipped) for (i = UNDEFINED_COLUMN+1; i <= NUMBER_REQUIRED_COLUMNS; i++) { addColumnToDesc(requiredTD, PredefinedColumns(i)); } requiredTD_p=new TableDesc(requiredTD); } } MSFlagCmd MSFlagCmd::referenceCopy(const String& newTableName, const Block& writableColumns) const { return MSFlagCmd(MSTable:: referenceCopy(newTableName,writableColumns)); } } //# NAMESPACE CASACORE - END casacore-2.4.1/ms/MeasurementSets/MSFlagCmd.h000066400000000000000000000115551321422335000207510ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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, uInt 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 void init(); private: // required by the need to throw an exception in the destructor Bool hasBeenDestroyed_p; }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/ms/MeasurementSets/MSFlagCmdColumns.cc000066400000000000000000000127701321422335000224500ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN ROMSFlagCmdColumns::ROMSFlagCmdColumns(const MSFlagCmd& msFlagCmd): applied_p(msFlagCmd, MSFlagCmd::columnName(MSFlagCmd::APPLIED)), command_p(msFlagCmd, MSFlagCmd::columnName(MSFlagCmd::COMMAND)), interval_p(msFlagCmd, MSFlagCmd::columnName(MSFlagCmd::INTERVAL)), level_p(msFlagCmd, MSFlagCmd::columnName(MSFlagCmd::LEVEL)), reason_p(msFlagCmd, MSFlagCmd::columnName(MSFlagCmd::REASON)), severity_p(msFlagCmd, MSFlagCmd::columnName(MSFlagCmd::SEVERITY)), time_p(msFlagCmd, MSFlagCmd::columnName(MSFlagCmd::TIME)), type_p(msFlagCmd, MSFlagCmd::columnName(MSFlagCmd::TYPE)), timeMeas_p(msFlagCmd, MSFlagCmd::columnName(MSFlagCmd::TIME)), intervalQuant_p(msFlagCmd, MSFlagCmd::columnName(MSFlagCmd::INTERVAL)), timeQuant_p(msFlagCmd, MSFlagCmd::columnName(MSFlagCmd::TIME)) {} ROMSFlagCmdColumns::~ROMSFlagCmdColumns() {} ROMSFlagCmdColumns::ROMSFlagCmdColumns(): applied_p(), command_p(), interval_p(), level_p(), reason_p(), severity_p(), time_p(), type_p(), timeMeas_p(), intervalQuant_p(), timeQuant_p() {} void ROMSFlagCmdColumns::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)); } MSFlagCmdColumns::MSFlagCmdColumns(MSFlagCmd& msFlagCmd): ROMSFlagCmdColumns(msFlagCmd), applied_p(msFlagCmd, MSFlagCmd::columnName(MSFlagCmd::APPLIED)), command_p(msFlagCmd, MSFlagCmd::columnName(MSFlagCmd::COMMAND)), interval_p(msFlagCmd, MSFlagCmd::columnName(MSFlagCmd::INTERVAL)), level_p(msFlagCmd, MSFlagCmd::columnName(MSFlagCmd::LEVEL)), reason_p(msFlagCmd, MSFlagCmd::columnName(MSFlagCmd::REASON)), severity_p(msFlagCmd, MSFlagCmd::columnName(MSFlagCmd::SEVERITY)), time_p(msFlagCmd, MSFlagCmd::columnName(MSFlagCmd::TIME)), type_p(msFlagCmd, MSFlagCmd::columnName(MSFlagCmd::TYPE)), timeMeas_p(msFlagCmd, MSFlagCmd::columnName(MSFlagCmd::TIME)), intervalQuant_p(msFlagCmd, MSFlagCmd::columnName(MSFlagCmd::INTERVAL)), timeQuant_p(msFlagCmd, MSFlagCmd::columnName(MSFlagCmd::TIME)) {} MSFlagCmdColumns::~MSFlagCmdColumns() {} void MSFlagCmdColumns::setEpochRef(MEpoch::Types ref, Bool tableMustBeEmpty){ timeMeas_p.setDescRefCode(ref, tableMustBeEmpty); } MSFlagCmdColumns::MSFlagCmdColumns(): ROMSFlagCmdColumns(), applied_p(), command_p(), interval_p(), level_p(), reason_p(), severity_p(), time_p(), type_p(), timeMeas_p(), intervalQuant_p(), timeQuant_p() {} void MSFlagCmdColumns::attach(MSFlagCmd& msFlagCmd) { ROMSFlagCmdColumns::attach(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)); } // Local Variables: // compile-command: "gmake MSFlagCmdColumns" // End: } //# NAMESPACE CASACORE - END casacore-2.4.1/ms/MeasurementSets/MSFlagCmdColumns.h000066400000000000000000000223301321422335000223030ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 read-only access to MSFlagCmd columns // // // // // //
      • MSFlagCmd //
      • ScalarColumn // // // // ROMSFlagCmdColumns stands for Read-Only MeasurementSet FlagCmd Table columns. // // // // This class provides read-only 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 ROMSColumns for // an example. // // // // See MSColumns for the motivation. // class ROMSFlagCmdColumns { public: // Create a columns object that accesses the data in the specified Table ROMSFlagCmdColumns(const MSFlagCmd& msFlagCmd); // The destructor does nothing special ~ROMSFlagCmdColumns(); // Access to required columns // const ROScalarColumn& applied() const {return applied_p;} const ROScalarColumn& command() const {return command_p;} const ROScalarQuantColumn& intervalQuant() const { return intervalQuant_p;} const ROScalarColumn& interval() const {return interval_p;} const ROScalarColumn& level() const {return level_p;} const ROScalarColumn& reason() const {return reason_p;} const ROScalarColumn& severity() const {return severity_p;} const ROScalarColumn& time() const {return time_p;} const ROScalarQuantColumn& timeQuant() const { return timeQuant_p;} const ROScalarMeasColumn& timeMeas() const { return timeMeas_p;} const ROScalarColumn& type() const {return type_p;} // // Convenience function that returns the number of rows in any of the columns uInt nrow() const {return applied_p.nrow();} protected: //# default constructor creates a object that is not usable. Use the attach //# function correct this. ROMSFlagCmdColumns(); //# 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. ROMSFlagCmdColumns(const ROMSFlagCmdColumns&); ROMSFlagCmdColumns& operator=(const ROMSFlagCmdColumns&); //# required columns ROScalarColumn applied_p; ROScalarColumn command_p; ROScalarColumn interval_p; ROScalarColumn level_p; ROScalarColumn reason_p; ROScalarColumn severity_p; ROScalarColumn time_p; ROScalarColumn type_p; //# Access to Measure columns ROScalarMeasColumn timeMeas_p; //# Access to Quantum columns ROScalarQuantColumn intervalQuant_p; ROScalarQuantColumn timeQuant_p; }; // // A class to provide easy read-write 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 ROMSFlagCmdColumns { public: // Create a columns object that accesses the data in the specified Table MSFlagCmdColumns(MSFlagCmd& msFlagCmd); // The destructor does nothing special ~MSFlagCmdColumns(); // Read-write 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;} // // Read-only access to required columns // const ROScalarColumn& applied() const { return ROMSFlagCmdColumns::applied();} const ROScalarColumn& command() const { return ROMSFlagCmdColumns::command();} const ROScalarQuantColumn& intervalQuant() const { return ROMSFlagCmdColumns::intervalQuant();} const ROScalarColumn& interval() const { return ROMSFlagCmdColumns::interval();} const ROScalarColumn& level() const { return ROMSFlagCmdColumns::level();} const ROScalarColumn& reason() const { return ROMSFlagCmdColumns::reason();} const ROScalarColumn& severity() const { return ROMSFlagCmdColumns::severity();} const ROScalarColumn& time() const { return ROMSFlagCmdColumns::time();} const ROScalarQuantColumn& timeQuant() const { return ROMSFlagCmdColumns::timeQuant();} const ROScalarMeasColumn& timeMeas() const { return ROMSFlagCmdColumns::timeMeas();} const ROScalarColumn& type() const { return ROMSFlagCmdColumns::type();} // // 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(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; }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/ms/MeasurementSets/MSFlagCmdEnums.h000066400000000000000000000065271321422335000217640ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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-2.4.1/ms/MeasurementSets/MSFreqOffColumns.cc000066400000000000000000000122331321422335000224750ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN ROMSFreqOffsetColumns:: ROMSFreqOffsetColumns(const MSFreqOffset& msFreqOffset): isNull_p(True), antenna1_p(), antenna2_p(), feedId_p(), interval_p(), offset_p(), spectralWindowId_p(), time_p(), timeMeas_p(), intervalQuant_p(), offsetQuant_p(), timeQuant_p() { attach(msFreqOffset); } ROMSFreqOffsetColumns::~ROMSFreqOffsetColumns() {} ROMSFreqOffsetColumns::ROMSFreqOffsetColumns(): isNull_p(True), antenna1_p(), antenna2_p(), feedId_p(), interval_p(), offset_p(), spectralWindowId_p(), time_p(), timeMeas_p(), intervalQuant_p(), offsetQuant_p(), timeQuant_p() { } void ROMSFreqOffsetColumns::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)); } } MSFreqOffsetColumns::MSFreqOffsetColumns(MSFreqOffset& msFreqOffset): ROMSFreqOffsetColumns(), antenna1_p(), antenna2_p(), feedId_p(), interval_p(), offset_p(), spectralWindowId_p(), time_p(), timeMeas_p(), intervalQuant_p(), offsetQuant_p(), timeQuant_p() { attach(msFreqOffset); } MSFreqOffsetColumns::~MSFreqOffsetColumns() {} void MSFreqOffsetColumns:: setEpochRef(MEpoch::Types ref, Bool tableMustBeEmpty) { timeMeas_p.setDescRefCode(ref, tableMustBeEmpty); } MSFreqOffsetColumns::MSFreqOffsetColumns(): ROMSFreqOffsetColumns(), antenna1_p(), antenna2_p(), feedId_p(), interval_p(), offset_p(), spectralWindowId_p(), time_p(), timeMeas_p(), intervalQuant_p(), offsetQuant_p(), timeQuant_p() { } void MSFreqOffsetColumns::attach(MSFreqOffset& msFreqOffset) { ROMSFreqOffsetColumns::attach(msFreqOffset); 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)); } } // Local Variables: // compile-command: "gmake MSFreqOffColumns" // End: } //# NAMESPACE CASACORE - END casacore-2.4.1/ms/MeasurementSets/MSFreqOffColumns.h000066400000000000000000000233541321422335000223450ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 read-only access to MSFreqOffset columns // // // // // //
      • MSFreqOffset //
      • ArrayColumn //
      • ScalarColumn // // // // ROMSFreqOffsetColumns stands for Read-Only MeasurementSet FreqOffset // Table columns. // // // // This class provides read-only 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 // ROMSColumns for an example. // // // // See MSColumns for the motivation. // class ROMSFreqOffsetColumns { public: // Create a columns object that accesses the data in the specified Table ROMSFreqOffsetColumns(const MSFreqOffset& msFreqOffset); // The destructor does nothing special ~ROMSFreqOffsetColumns(); // Is this object defined? (MSFreqOffset table is optional) Bool isNull() const {return isNull_p;} // Access to columns // const ROScalarColumn& antenna1() const {return antenna1_p;} const ROScalarColumn& antenna2() const {return antenna2_p;} const ROScalarColumn& feedId() const {return feedId_p;} const ROScalarColumn& interval() const {return interval_p;} const ROScalarQuantColumn& intervalQuant() const { return intervalQuant_p;} const ROScalarColumn& offset() const {return offset_p;} const ROScalarQuantColumn& offsetQuant() const { return offsetQuant_p;} const ROScalarColumn& spectralWindowId() const { return spectralWindowId_p;} const ROScalarColumn& time() const {return time_p;} const ROScalarQuantColumn& timeQuant() const {return timeQuant_p;} const ROScalarMeasColumn& 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. uInt nrow() const {return isNull() ? 0 : antenna1_p.nrow();} protected: //# default constructor creates a object that is not usable. Use the attach //# function correct this. ROMSFreqOffsetColumns(); //# 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. ROMSFreqOffsetColumns(const ROMSFreqOffsetColumns&); ROMSFreqOffsetColumns& operator=(const ROMSFreqOffsetColumns&); //# Is the object not attached to a Table. Bool isNull_p; //# required columns ROScalarColumn antenna1_p; ROScalarColumn antenna2_p; ROScalarColumn feedId_p; ROScalarColumn interval_p; ROScalarColumn offset_p; ROScalarColumn spectralWindowId_p; ROScalarColumn time_p; //# Access to Measure columns ROScalarMeasColumn timeMeas_p; //# Access to Quantum columns ROScalarQuantColumn intervalQuant_p; ROScalarQuantColumn offsetQuant_p; ROScalarQuantColumn timeQuant_p; }; // // A class to provide easy read-write 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 ROMSFreqOffsetColumns { public: // Create a columns object that accesses the data in the specified Table MSFreqOffsetColumns(MSFreqOffset& msFreqOffset); // The destructor does nothing special ~MSFreqOffsetColumns(); // Read-write 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;} // // Read-only access to required columns // const ROScalarColumn& antenna1() const { return ROMSFreqOffsetColumns::antenna1();} const ROScalarColumn& antenna2() const { return ROMSFreqOffsetColumns::antenna2();} const ROScalarColumn& feedId() const { return ROMSFreqOffsetColumns::feedId();} const ROScalarColumn& interval() const { return ROMSFreqOffsetColumns::interval();} const ROScalarQuantColumn& intervalQuant() const { return ROMSFreqOffsetColumns::intervalQuant();} const ROScalarColumn& offset() const { return ROMSFreqOffsetColumns::offset();} const ROScalarQuantColumn& offsetQuant() const { return ROMSFreqOffsetColumns::offsetQuant();} const ROScalarColumn& spectralWindowId() const { return ROMSFreqOffsetColumns::spectralWindowId();} const ROScalarColumn& time() const { return ROMSFreqOffsetColumns::time();} const ROScalarQuantColumn& timeQuant() const { return ROMSFreqOffsetColumns::timeQuant();} const ROScalarMeasColumn& timeMeas() const { return ROMSFreqOffsetColumns::timeMeas();} // // 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(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&); //# 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; }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/ms/MeasurementSets/MSFreqOffEnums.h000066400000000000000000000063401321422335000220100ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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-2.4.1/ms/MeasurementSets/MSFreqOffset.cc000066400000000000000000000135371321422335000216600ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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, uInt 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 &, uInt, 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; } void MSFreqOffset::init() { if (! columnMap_p.ndefined()) { // the PredefinedColumns // ANTENNA1 colMapDef(ANTENNA1, "ANTENNA1", TpInt, "Antenna1 id","",""); // ANTENNA2 colMapDef(ANTENNA2, "ANTENNA2", TpInt, "Antenna2 id","",""); // FEED_ID colMapDef(FEED_ID, "FEED_ID", TpInt, "Feed id","",""); // INTERVAL colMapDef(INTERVAL, "INTERVAL", TpDouble, "Time interval","s",""); // OFFSET colMapDef(OFFSET, "OFFSET", TpDouble, "Frequency offset - antenna based","Hz",""); // SPECTRAL_WINDOW_ID colMapDef(SPECTRAL_WINDOW_ID, "SPECTRAL_WINDOW_ID", TpInt, "Spectral window id","",""); // TIME colMapDef(TIME, "TIME", TpDouble, "Midpoint of interval","s","Epoch"); // PredefinedKeywords // init requiredTableDesc TableDesc requiredTD; // all required keywords uInt i; for (i = UNDEFINED_KEYWORD+1; i <= NUMBER_PREDEFINED_KEYWORDS; i++) { addKeyToDesc(requiredTD, PredefinedKeywords(i)); } // all required columns // Now define all other columns (duplicates are skipped) for (i = UNDEFINED_COLUMN+1; i <= NUMBER_REQUIRED_COLUMNS; i++) { addColumnToDesc(requiredTD, PredefinedColumns(i)); } requiredTD_p=new TableDesc(requiredTD); } } MSFreqOffset MSFreqOffset::referenceCopy(const String& newTableName, const Block& writableColumns) const { return MSFreqOffset(MSTable:: referenceCopy(newTableName,writableColumns)); } } //# NAMESPACE CASACORE - END casacore-2.4.1/ms/MeasurementSets/MSFreqOffset.h000066400000000000000000000116761321422335000215240ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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, uInt 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 void init(); private: // required by the need to throw an exception in the destructor Bool hasBeenDestroyed_p; }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/ms/MeasurementSets/MSHistory.cc000066400000000000000000000142161321422335000212500ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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, uInt 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; } void MSHistory::init() { if (! columnMap_p.ndefined()) { // the PredefinedColumns // APPLICATION colMapDef(APPLICATION,"APPLICATION",TpString, "Application name","",""); // APP_PARAMS colMapDef(APP_PARAMS,"APP_PARAMS",TpArrayString, "Application parameters","",""); // CLI_COMMAND colMapDef(CLI_COMMAND,"CLI_COMMAND",TpArrayString, "CLI command sequence","",""); // MESSAGE colMapDef(MESSAGE,"MESSAGE",TpString, "Log message","",""); // OBJECT_ID colMapDef(OBJECT_ID,"OBJECT_ID",TpInt, "Originating ObjectID","",""); // OBSERVATION_ID colMapDef(OBSERVATION_ID, "OBSERVATION_ID", TpInt, "Observation id (index in OBSERVATION table)","",""); // ORIGIN colMapDef(ORIGIN,"ORIGIN",TpString, "(Source code) origin from which message originated","",""); // PRIORITY colMapDef(PRIORITY,"PRIORITY",TpString, "Message priority","",""); // TIME colMapDef(TIME,"TIME",TpDouble, "Timestamp of message","s","Epoch"); // PredefinedKeywords // init requiredTableDesc TableDesc requiredTD; // all required keywords uInt i; for (i = UNDEFINED_KEYWORD+1; i <= NUMBER_PREDEFINED_KEYWORDS; i++) { addKeyToDesc(requiredTD, PredefinedKeywords(i)); } // define the columns with known dimensionality addColumnToDesc(requiredTD, APP_PARAMS, 1); addColumnToDesc(requiredTD, CLI_COMMAND, 1); // all required columns for (i = UNDEFINED_COLUMN+1; i <= NUMBER_REQUIRED_COLUMNS; i++) { addColumnToDesc(requiredTD, PredefinedColumns(i)); } requiredTD_p=new TableDesc(requiredTD); } } MSHistory MSHistory::referenceCopy(const String& newTableName, const Block& writableColumns) const { return MSHistory(MSTable::referenceCopy (newTableName,writableColumns)); } } //# NAMESPACE CASACORE - END casacore-2.4.1/ms/MeasurementSets/MSHistory.h000066400000000000000000000115541321422335000211140ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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, uInt 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 void init(); private: // required by the need to throw an exception in the destructor Bool hasBeenDestroyed_p; }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/ms/MeasurementSets/MSHistoryColumns.cc000066400000000000000000000133621321422335000226120ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN ROMSHistoryColumns::ROMSHistoryColumns(const MSHistory& msHistory): application_p(msHistory, MSHistory:: columnName(MSHistory::APPLICATION)), appParams_p(msHistory, MSHistory::columnName(MSHistory::APP_PARAMS)), cliCommand_p(msHistory, MSHistory::columnName(MSHistory::CLI_COMMAND)), message_p(msHistory, MSHistory::columnName(MSHistory::MESSAGE)), objectId_p(msHistory, MSHistory::columnName(MSHistory::OBJECT_ID)), observationId_p(msHistory, MSHistory:: columnName(MSHistory::OBSERVATION_ID)), origin_p(msHistory, MSHistory::columnName(MSHistory::ORIGIN)), priority_p(msHistory, MSHistory::columnName(MSHistory::PRIORITY)), time_p(msHistory, MSHistory::columnName(MSHistory::TIME)), timeMeas_p(msHistory, MSHistory::columnName(MSHistory::TIME)), timeQuant_p(msHistory, MSHistory::columnName(MSHistory::TIME)) {} ROMSHistoryColumns::~ROMSHistoryColumns() {} ROMSHistoryColumns::ROMSHistoryColumns(): application_p(), appParams_p(), cliCommand_p(), message_p(), objectId_p(), observationId_p(), origin_p(), priority_p(), time_p(), timeMeas_p(), timeQuant_p() {} void ROMSHistoryColumns::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)); } MSHistoryColumns::MSHistoryColumns(MSHistory& msHistory): ROMSHistoryColumns(msHistory), application_p(msHistory, MSHistory:: columnName(MSHistory::APPLICATION)), appParams_p(msHistory, MSHistory::columnName(MSHistory::APP_PARAMS)), cliCommand_p(msHistory, MSHistory::columnName(MSHistory::CLI_COMMAND)), message_p(msHistory, MSHistory::columnName(MSHistory::MESSAGE)), objectId_p(msHistory, MSHistory::columnName(MSHistory::OBJECT_ID)), observationId_p(msHistory, MSHistory:: columnName(MSHistory::OBSERVATION_ID)), origin_p(msHistory, MSHistory::columnName(MSHistory::ORIGIN)), priority_p(msHistory, MSHistory::columnName(MSHistory::PRIORITY)), time_p(msHistory, MSHistory::columnName(MSHistory::TIME)), timeMeas_p(msHistory, MSHistory::columnName(MSHistory::TIME)), timeQuant_p(msHistory, MSHistory::columnName(MSHistory::TIME)) {} MSHistoryColumns::~MSHistoryColumns() {} void MSHistoryColumns::setEpochRef(MEpoch::Types ref, Bool tableMustBeEmpty){ timeMeas_p.setDescRefCode(ref, tableMustBeEmpty); } MSHistoryColumns::MSHistoryColumns(): ROMSHistoryColumns(), application_p(), appParams_p(), cliCommand_p(), message_p(), objectId_p(), observationId_p(), origin_p(), priority_p(), time_p(), timeMeas_p(), timeQuant_p() {} void MSHistoryColumns::attach(MSHistory& msHistory) { ROMSHistoryColumns::attach(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)); } // Local Variables: // compile-command: "gmake MSHistoryColumns" // End: } //# NAMESPACE CASACORE - END casacore-2.4.1/ms/MeasurementSets/MSHistoryColumns.h000066400000000000000000000231661321422335000224570ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 read-only access to MSHistory columns // // // // // //
      • MSHistory //
      • ArrayColumn //
      • ScalarColumn // // // // ROMSHistoryColumns stands for Read-Only MeasurementSet History Table columns. // // // // This class provides read-only 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 // ROMSColumns for an example. // // // // See MSColumns for the motivation. // class ROMSHistoryColumns { public: // Create a columns object that accesses the data in the specified Table ROMSHistoryColumns(const MSHistory& msHistory); // The destructor does nothing special ~ROMSHistoryColumns(); // Access to required columns // const ROScalarColumn& application() const {return application_p;} const ROArrayColumn& appParams() const {return appParams_p;} const ROArrayColumn& cliCommand() const {return cliCommand_p;} const ROScalarColumn& message() const {return message_p;} const ROScalarColumn& objectId() const {return objectId_p;} const ROScalarColumn& observationId() const {return observationId_p;} const ROScalarColumn& origin() const {return origin_p;} const ROScalarColumn& priority() const {return priority_p;} const ROScalarColumn& time() const {return time_p;} const ROScalarQuantColumn& timeQuant() const {return timeQuant_p;} const ROScalarMeasColumn& timeMeas() const {return timeMeas_p;} // // Convenience function that returns the number of rows in any of the columns uInt nrow() const {return application_p.nrow();} protected: //# default constructor creates a object that is not usable. Use the attach //# function correct this. ROMSHistoryColumns(); //# 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. ROMSHistoryColumns(const ROMSHistoryColumns&); ROMSHistoryColumns& operator=(const ROMSHistoryColumns&); //# required columns ROScalarColumn application_p; ROArrayColumn appParams_p; ROArrayColumn cliCommand_p; ROScalarColumn message_p; ROScalarColumn objectId_p; ROScalarColumn observationId_p; ROScalarColumn origin_p; ROScalarColumn priority_p; ROScalarColumn time_p; //# Access to Measure columns ROScalarMeasColumn timeMeas_p; //# Access to Quantum columns ROScalarQuantColumn timeQuant_p; }; // // A class to provide easy read-write 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 ROMSHistoryColumns { public: // Create a columns object that accesses the data in the specified Table MSHistoryColumns(MSHistory& msHistory); // The destructor does nothing special ~MSHistoryColumns(); // Read-write 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;} // // Read-only access to required columns // const ROScalarColumn& application() const { return ROMSHistoryColumns::application();} const ROArrayColumn& appParams() const { return ROMSHistoryColumns::appParams();} const ROArrayColumn& cliCommand() const { return ROMSHistoryColumns::cliCommand();} const ROScalarColumn& message() const { return ROMSHistoryColumns::message();} const ROScalarColumn& objectId() const { return ROMSHistoryColumns::objectId();} const ROScalarColumn& observationId() const { return ROMSHistoryColumns::observationId();} const ROScalarColumn& origin() const { return ROMSHistoryColumns::origin();} const ROScalarColumn& priority() const { return ROMSHistoryColumns::priority();} const ROScalarColumn& time() const { return ROMSHistoryColumns::time();} const ROScalarQuantColumn& timeQuant() const { return ROMSHistoryColumns::timeQuant();} const ROScalarMeasColumn& timeMeas() const { return ROMSHistoryColumns::timeMeas();} // // 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(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; }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/ms/MeasurementSets/MSHistoryEnums.h000066400000000000000000000067441321422335000221310ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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-2.4.1/ms/MeasurementSets/MSHistoryHandler.cc000066400000000000000000000137611321422335000225520ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN MSHistoryHandler::MSHistoryHandler(MeasurementSet& ms, String app){ histTable_p = ms.history(); msHistCol_p= new MSHistoryColumns(histTable_p); application_p=app; } MSHistoryHandler &MSHistoryHandler::operator=(MSHistoryHandler& other){ if(this != &other){ histTable_p=other.histTable_p; msHistCol_p=other.msHistCol_p; application_p=other.application_p; } return *this; } MSHistoryHandler::~MSHistoryHandler(){ delete msHistCol_p; } void MSHistoryHandler::addMessage(MeasurementSet& ms, String message, String app, String cliComm, String origin){ if (message.length() == 0 && cliComm.length() == 0) { // No need to record an entry. return; } MSHistory &histTable=ms.history(); Int 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(String message, String cliComm, String origin){ if (message.length() == 0 && cliComm.length() == 0) { // No need to record an entry. return; } Int 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); histTable_p.flush(); } void MSHistoryHandler::addMessage(LogSinkInterface& sink, String cliComm){ Int row = histTable_p.nrow(); uInt 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 (uInt 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(); histTable_p.flush(); } void MSHistoryHandler::addMessage(LogIO& os, String cliComm){ addMessage(os.localSink(), cliComm); } void MSHistoryHandler::cliCommand(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(""); Int 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(); histTable_p.flush(); } } //# NAMESPACE CASACORE - END casacore-2.4.1/ms/MeasurementSets/MSHistoryHandler.h000066400000000000000000000060521321422335000224070ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 // A couple of the simple methods are independent and can be called without // constructing. // class MSHistoryHandler { public: //Construct the history handler from an ms MSHistoryHandler(MeasurementSet& ms, String app=""); MSHistoryHandler &operator=(MSHistoryHandler &other); // Destructor ~MSHistoryHandler(); //Add a string message // This method does not need construction ...can be called explicitly // static void addMessage(MeasurementSet& ms, String message, String app="", String cliComm="", String origin=""); // Add message and/or CLI command to the history table void addMessage(String message, String cliComm="", String origin=""); // In this version the LogIO object need to have a valid LogSink with // messages in it. void addMessage(LogIO& message, String cliComm=""); void addMessage(LogSinkInterface& sink, String cliComm=""); void cliCommand(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-2.4.1/ms/MeasurementSets/MSIter.cc000066400000000000000000000724321321422335000205160ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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, Double timeInterval, Bool addDefaultSortColumns, Bool storeSorted) : curMS_p(0),lastMS_p(-1), 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), storeSorted_p(storeSorted), interval_p(timeInterval), prevFirstTimeStamp_p(-1.0) { construct(sortColumns,addDefaultSortColumns); } Bool MSIter::isSubSet (const Vector& r1, const Vector& r2) { Int n1 = r1.nelements(); Int 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 (Int 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"); uInt n=colNames.nelements(); cols.resize(n); for (uInt i=0; i0 && cols[i] columns; Int iCol=0; if (addDefaultSortColumns) { columns.resize(cols.nelements()+4-nCol); if (!arraySeen) { // add array if it's not there columns[iCol++]=MS::columnName(MS::ARRAY_ID); } if (!fieldSeen) { // add field if it's not there columns[iCol++]=MS::columnName(MS::FIELD_ID); } if (!ddSeen) { // add dd if it's not there columns[iCol++]=MS::columnName(MS::DATA_DESC_ID); } if (!timeSeen) { // add time if it's not there columns[iCol++]=MS::columnName(MS::TIME); timeSeen = True; } } else { columns.resize(cols.nelements()); } for (uInt i=0; i > objComp(columns.nelements()); for (uInt i=0; i orders(columns.nelements(),TableIterator::Ascending); // Store the sorted table for future access if possible, // reuse it if already there for (Int i=0; i(columns))) { // 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 if (aips_debug) cout << "MSIter::construct - resorting table"<(columns)); } // 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 (Int 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(); curArray_p = other.curArray_p; lastArray_p = other.lastArray_p; curSource_p = other.curSource_p; curFieldName_p = other.curFieldName_p; curSourceName_p = other.curSourceName_p; curField_p = other.curField_p; lastField_p = other.lastField_p; curSpectralWindow_p = other.curSpectralWindow_p; lastSpectralWindow_p = other.lastSpectralWindow_p; curPolarizationId_p = other.curPolarizationId_p; lastPolarizationId_p = other.lastPolarizationId_p; curDataDescId_p = other.curDataDescId_p; lastDataDescId_p = other.lastDataDescId_p; more_p = other.more_p; newMS_p = other.newMS_p; newArray_p = other.newArray_p; newField_p = other.newField_p; newSpectralWindow_p = other.newSpectralWindow_p; newPolarizationId_p = other.newPolarizationId_p; newDataDescId_p = other.newDataDescId_p; preselected_p = other.preselected_p; timeDepFeed_p = other.timeDepFeed_p; spwDepFeed_p = other.spwDepFeed_p; checkFeed_p = other.checkFeed_p; startChan_p = other.startChan_p; storeSorted_p = other.storeSorted_p; interval_p = other.interval_p; preselectedChanStart_p = other.preselectedChanStart_p; preselectednChan_p = other.preselectednChan_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.reset(new MSInterval(interval_p)); prevFirstTimeStamp_p=other.prevFirstTimeStamp_p; return *this; } MSIter * MSIter::clone() const { return new MSIter(*this); } const MS& MSIter::ms(const uInt 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=newArray_p=newSpectralWindow_p=newField_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=newArray_p=newSpectralWindow_p=newPolarizationId_p= newDataDescId_p=newField_p=checkFeed_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)); colDataDesc_p.attach(curTable_p,MS::columnName(MS::DATA_DESC_ID)); colField_p.attach(curTable_p,MS::columnName(MS::FIELD_ID)); // msc_p is already defined here (it is set in setMSInfo) if(newMS_p) msc_p->antenna().mount().getColumn(antennaMounts_p,True); setDataDescInfo(); setArrayInfo(); setFeedInfo(); setFieldInfo(); // 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) { This->freqCacheOK_p=True; Int spw = curSpectralWindow_p; if (preselected_p) { msc_p->spectralWindow().chanFreq(). getSlice(spw,Slicer(Slice(preselectedChanStart_p[spw], preselectednChan_p[spw])), This->frequency_p,True); } else { msc_p->spectralWindow().chanFreq(). get(spw,This->frequency_p,True); } } return frequency_p; } const MFrequency& MSIter::frequency0() const { // set the channel0 frequency measure This->frequency0_p= Vector(msc_p->spectralWindow(). chanFreqMeas()(curSpectralWindow_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; Int sourceId = msc_p->field().sourceId()(curField_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 != curMS_p); if (newMS_p) { lastMS_p = curMS_p; if (!tabIterAtStart_p[curMS_p]) tabIter_p[curMS_p]->reset(); msc_p = new ROMSColumns(bms_p[curMS_p]); // check to see if we are attached to a 'reference MS' with a // DATA column that is a selection of the original DATA const TableRecord & kws = (msc_p->data().isNull() ? msc_p->floatData().keywordSet() : msc_p->data().keywordSet()); preselected_p = kws.isDefined("CHANNEL_SELECTION"); if (preselected_p) { // get the selection Matrix selection; kws.get("CHANNEL_SELECTION",selection); Int nSpw = selection.ncolumn(); preselectedChanStart_p.resize(nSpw); preselectednChan_p.resize(nSpw); for (Int i = 0; i < nSpw; i++) { preselectedChanStart_p[i] = selection(0,i); preselectednChan_p[i] = selection(1,i); } } // 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 lastSpectralWindow_p=-1; lastArray_p=-1; lastPolarizationId_p=-1; lastDataDescId_p=-1; lastField_p=-1; } } void MSIter::setArrayInfo() { // Set the array info curArray_p=colArray_p(0); newArray_p=(lastArray_p!=curArray_p); if (newArray_p) { lastArray_p=curArray_p; } } void MSIter::setFeedInfo() { // 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 timeDepFeed_p=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_p=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()) { ROMSFeedColumns 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_p=!unique; } Vector spwId=msc_p->feed().spectralWindowId().getColumn(); spwDepFeed_p = !(allEQ(spwId,-1)); first=True; checkFeed_p = False; if (timeDepFeed_p) { 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 && newSpectralWindow_p) || first) { 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 (uInt 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); // } } void MSIter::setDataDescInfo() { curDataDescId_p = colDataDesc_p(0); curSpectralWindow_p = msc_p->dataDescription().spectralWindowId() (curDataDescId_p); curPolarizationId_p = msc_p->dataDescription().polarizationId() (curDataDescId_p); newDataDescId_p=(lastDataDescId_p!=curDataDescId_p); if (newDataDescId_p) lastDataDescId_p=curDataDescId_p; newSpectralWindow_p=(lastSpectralWindow_p!=curSpectralWindow_p); newPolarizationId_p=(lastPolarizationId_p!=curPolarizationId_p); if (newSpectralWindow_p) { lastSpectralWindow_p = curSpectralWindow_p; startChan_p= (preselected_p ? preselectedChanStart_p[curSpectralWindow_p] : 0); freqCacheOK_p=False; } if (newPolarizationId_p) { lastPolarizationId_p = curPolarizationId_p; polFrame_p=Circular; Int polType = Vector(msc_p->polarization(). corrType()(curPolarizationId_p))(0); if (polType>=Stokes::XX && polType<=Stokes::YY) polFrame_p=Linear; } } void MSIter::setFieldInfo() { curField_p=colField_p(0); newField_p=(lastField_p!=curField_p); if (newField_p) { lastField_p = curField_p; } } const String& MSIter::fieldName() const { if(newField_p) This->curFieldName_p = msc_p->field().name()(curField_p); return curFieldName_p; } const String& MSIter::sourceName() const { if(newField_p){ // Retrieve source name, if specified. This->curSourceName_p = ""; if (curSource_p >= 0 && !msc_p->source().sourceId().isNull()) { Vector sourceId=msc_p->source().sourceId().getColumn(); uInt i=0; Bool found=False; while (i < sourceId.nelements() && !found) { if (sourceId(i)==curSource_p) { found=True; This->curSourceName_p=msc_p->source().name()(i); } i++; } } } return curSourceName_p; } const MDirection& MSIter::phaseCenter() const { if(msc_p){ Double firstTimeStamp=ROScalarColumn(curTable_p, MS::columnName(MS::TIME)).get(0); if(newField_p || (firstTimeStamp != prevFirstTimeStamp_p)){ This->prevFirstTimeStamp_p=firstTimeStamp; This->phaseCenter_p=msc_p->field().phaseDirMeas(curField_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 (Int 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-2.4.1/ms/MeasurementSets/MSIter.h000066400000000000000000000454701321422335000203620ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 ROMSColumns; 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); // 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 ROMSColumns const ROMSColumns& msColumns() const; // Return the current MS Id (according to the order in which // they appeared in the constructor) Int msId() const; // Return true if msId has changed since last iteration Bool newMS() const; // Return the current ArrayId Int arrayId() const; // Return True if ArrayId has changed since last iteration Bool newArray() const; // Return the current FieldId Int fieldId() const; // Return True if FieldId/Source has changed since last iteration Bool newField() const; // Return current SpectralWindow Int spectralWindowId() const; // Return True if SpectralWindow has changed since last iteration Bool newSpectralWindow() const; // Return current DataDescriptionId Int dataDescriptionId() const; // Return True if DataDescriptionId has changed since last iteration Bool newDataDescriptionId() const; // Return current PolarizationId Int polarizationId() const; // Return True if polarization has changed since last iteration Bool newPolarizationId() const; // Return frame for polarization (returns PolFrame enum) Int polFrame() const; // Return the frequencies corresponding to the DATA matrix. const Vector& frequency() const; // Return frequency of first channel 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 the channel number of the first channel in the DATA. // (non-zero for reference MS created by VisSet with channel selection) Int startChan() 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 Int 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 uInt 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); // advance the iteration void advance(); // set the iteration state virtual void setState(); void setMSInfo(); void setArrayInfo(); void setFeedInfo(); void setDataDescInfo(); void setFieldInfo(); // 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; Int nMS_p; CountedPtr msc_p; Table curTable_p; Int curMS_p, lastMS_p, curArray_p, lastArray_p, curSource_p; String curFieldName_p, curSourceName_p; Int curField_p, lastField_p, curSpectralWindow_p, lastSpectralWindow_p; Int curPolarizationId_p, lastPolarizationId_p; Int curDataDescId_p, lastDataDescId_p; Bool more_p, newMS_p, newArray_p, newField_p, newSpectralWindow_p, newPolarizationId_p, newDataDescId_p, preselected_p, timeDepFeed_p, spwDepFeed_p, checkFeed_p; Int startChan_p; // Globally control disk storage of SORTED_TABLE Bool storeSorted_p; // time selection Double interval_p; // channel selection Block preselectedChanStart_p,preselectednChan_p; // columns ScalarColumn colArray_p, colDataDesc_p, colField_p; MDirection phaseCenter_p; Double prevFirstTimeStamp_p; //cache for access functions Matrix receptorAnglesFeed0_p; // former receptorAngle_p, // temporary retained for compatibility // contain actually a reference to the // first plane of receptorAngles_p Cube receptorAngles_p; Vector > CJonesFeed0_p; // a temporary reference // similar to receptorAngle_p Matrix > CJones_p; Vector antennaMounts_p; // a string mount identifier for each // antenna (e.g. EQUATORIAL, ALT-AZ,...) 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. Bool allBeamOffsetsZero_p; // True if all elements of beamOffsets_p // are zero (to speed things up in a // single beam case) PolFrame polFrame_p; Bool freqCacheOK_p; Vector frequency_p; MFrequency frequency0_p; MFrequency restFrequency_p; MPosition telescopePosition_p; CountedPtr 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 ROMSColumns& MSIter::msColumns() const { return *msc_p;} inline Bool MSIter::newMS() const { return newMS_p;} inline Bool MSIter::newArray() const {return newArray_p;} inline Bool MSIter::newField() const { return newField_p;} inline Bool MSIter::newSpectralWindow() const { return newSpectralWindow_p;} inline Int MSIter::msId() const { return curMS_p;} inline Int MSIter::numMS() const { return nMS_p;} inline Int MSIter::arrayId() const {return curArray_p;} inline Int MSIter::fieldId() const { return curField_p;} inline Int MSIter::spectralWindowId() const { return curSpectralWindow_p;} inline Int MSIter::polarizationId() const {return curPolarizationId_p;} inline Int MSIter::dataDescriptionId() const {return curDataDescId_p;} inline Bool MSIter::newPolarizationId() const { return newPolarizationId_p;} inline Bool MSIter::newDataDescriptionId() const { return newDataDescId_p;} inline Int MSIter::polFrame() const { return polFrame_p;} inline const MPosition& MSIter::telescopePosition() const { return telescopePosition_p;} inline const Vector >& MSIter::CJones() const { return CJonesFeed0_p;} inline const Matrix >& MSIter::CJonesAll() const { return CJones_p;} inline const Matrix& MSIter::receptorAngle() const {return receptorAnglesFeed0_p;} inline const Cube& MSIter::receptorAngles() const {return receptorAngles_p;} inline const Vector& MSIter::antennaMounts() const {return antennaMounts_p;} inline const Cube >& MSIter::getBeamOffsets() const {return beamOffsets_p;} inline Int MSIter::startChan() const {return startChan_p;} inline Bool MSIter::allBeamOffsetsZero() const {return allBeamOffsetsZero_p;} } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/ms/MeasurementSets/MSMainColumns.cc000066400000000000000000000377311321422335000220430ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN ROMSMainColumns::ROMSMainColumns(const MeasurementSet& ms): antenna1_p(ms, MS::columnName(MS::ANTENNA1)), antenna2_p(ms, MS::columnName(MS::ANTENNA2)), arrayId_p(ms, MS::columnName(MS::ARRAY_ID)), dataDescId_p(ms, MS::columnName(MS::DATA_DESC_ID)), exposure_p(ms, MS::columnName(MS::EXPOSURE)), feed1_p(ms, MS::columnName(MS::FEED1)), feed2_p(ms, MS::columnName(MS::FEED2)), fieldId_p(ms, MS::columnName(MS::FIELD_ID)), flag_p(ms, MS::columnName(MS::FLAG)), flagCategory_p(ms, MS::columnName(MS::FLAG_CATEGORY)), flagRow_p(ms, MS::columnName(MS::FLAG_ROW)), interval_p(ms, MS::columnName(MS::INTERVAL)), observationId_p(ms, MS::columnName(MS::OBSERVATION_ID)), processorId_p(ms, MS::columnName(MS::PROCESSOR_ID)), scanNumber_p(ms, MS::columnName(MS::SCAN_NUMBER)), sigma_p(ms, MS::columnName(MS::SIGMA)), stateId_p(ms, MS::columnName(MS::STATE_ID)), time_p(ms, MS::columnName(MS::TIME)), timeCentroid_p(ms, MS::columnName(MS::TIME_CENTROID)), uvw_p(ms, MS::columnName(MS::UVW)), weight_p(ms, MS::columnName(MS::WEIGHT)), antenna3_p(), baselineRef_p(), data_p(), feed3_p(), floatData_p(), lagData_p(), phaseId_p(), pulsarBin_p(), pulsarGateId_p(), sigmaSpectrum_p(), timeExtraPrec_p(), uvw2_p(), videoPoint_p(), weightSpectrum_p(), weightSpectrumCorrected_p(), correctedData_p(), imagingWeight_p(), modelData_p(), timeMeas_p(ms, MS::columnName(MS::TIME)), timeCentroidMeas_p(ms, MS::columnName(MS::TIME_CENTROID)), uvwMeas_p(ms, MS::columnName(MS::UVW)), uvw2Meas_p(), exposureQuant_p(ms, MS::columnName(MS::EXPOSURE)), intervalQuant_p(ms, MS::columnName(MS::INTERVAL)), timeQuant_p(ms, MS::columnName(MS::TIME)), timeCentroidQuant_p(ms, MS::columnName(MS::TIME_CENTROID)), uvwQuant_p(ms, MS::columnName(MS::UVW)), timeExtraPrecQuant_p(), uvw2Quant_p() { attachOptionalCols(ms); } ROMSMainColumns::~ROMSMainColumns() {} Vector ROMSMainColumns::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)); } ROMSMainColumns::ROMSMainColumns(): antenna1_p(), antenna2_p(), arrayId_p(), dataDescId_p(), exposure_p(), feed1_p(), feed2_p(), fieldId_p(), flag_p(), flagCategory_p(), flagRow_p(), interval_p(), observationId_p(), processorId_p(), scanNumber_p(), sigma_p(), stateId_p(), time_p(), timeCentroid_p(), uvw_p(), weight_p(), antenna3_p(), baselineRef_p(), data_p(), feed3_p(), floatData_p(), lagData_p(), phaseId_p(), pulsarBin_p(), pulsarGateId_p(), sigmaSpectrum_p(), timeExtraPrec_p(), uvw2_p(), videoPoint_p(), weightSpectrum_p(), weightSpectrumCorrected_p(), correctedData_p(), imagingWeight_p(), modelData_p(), timeMeas_p(), timeCentroidMeas_p(), uvwMeas_p(), uvw2Meas_p(), exposureQuant_p(), intervalQuant_p(), timeQuant_p(), timeCentroidQuant_p(), uvwQuant_p(), timeExtraPrecQuant_p(), uvw2Quant_p() { } void ROMSMainColumns::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)); attachOptionalCols(ms); } void ROMSMainColumns::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)); } } MSMainColumns::MSMainColumns(MeasurementSet& ms): ROMSMainColumns(ms), antenna1_p(ms, MS::columnName(MS::ANTENNA1)), antenna2_p(ms, MS::columnName(MS::ANTENNA2)), arrayId_p(ms, MS::columnName(MS::ARRAY_ID)), dataDescId_p(ms, MS::columnName(MS::DATA_DESC_ID)), exposure_p(ms, MS::columnName(MS::EXPOSURE)), feed1_p(ms, MS::columnName(MS::FEED1)), feed2_p(ms, MS::columnName(MS::FEED2)), fieldId_p(ms, MS::columnName(MS::FIELD_ID)), flag_p(ms, MS::columnName(MS::FLAG)), flagCategory_p(ms, MS::columnName(MS::FLAG_CATEGORY)), flagRow_p(ms, MS::columnName(MS::FLAG_ROW)), interval_p(ms, MS::columnName(MS::INTERVAL)), observationId_p(ms, MS::columnName(MS::OBSERVATION_ID)), processorId_p(ms, MS::columnName(MS::PROCESSOR_ID)), scanNumber_p(ms, MS::columnName(MS::SCAN_NUMBER)), sigma_p(ms, MS::columnName(MS::SIGMA)), stateId_p(ms, MS::columnName(MS::STATE_ID)), time_p(ms, MS::columnName(MS::TIME)), timeCentroid_p(ms, MS::columnName(MS::TIME_CENTROID)), uvw_p(ms, MS::columnName(MS::UVW)), weight_p(ms, MS::columnName(MS::WEIGHT)), antenna3_p(), baselineRef_p(), data_p(), feed3_p(), floatData_p(), lagData_p(), phaseId_p(), pulsarBin_p(), pulsarGateId_p(), sigmaSpectrum_p(), timeExtraPrec_p(), uvw2_p(), videoPoint_p(), weightSpectrum_p(), weightSpectrumCorrected_p(), correctedData_p(), imagingWeight_p(), modelData_p(), timeMeas_p(ms, MS::columnName(MS::TIME)), timeCentroidMeas_p(ms, MS::columnName(MS::TIME_CENTROID)), uvwMeas_p(ms, MS::columnName(MS::UVW)), uvw2Meas_p(), exposureQuant_p(ms, MS::columnName(MS::EXPOSURE)), intervalQuant_p(ms, MS::columnName(MS::INTERVAL)), timeQuant_p(ms, MS::columnName(MS::TIME)), timeCentroidQuant_p(ms, MS::columnName(MS::TIME_CENTROID)), uvwQuant_p(ms, MS::columnName(MS::UVW)), timeExtraPrecQuant_p(), uvw2Quant_p() { attachOptionalCols(ms); } MSMainColumns::~MSMainColumns() {} 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); } } MSMainColumns::MSMainColumns(): ROMSMainColumns(), antenna1_p(), antenna2_p(), arrayId_p(), dataDescId_p(), exposure_p(), feed1_p(), feed2_p(), fieldId_p(), flag_p(), flagCategory_p(), flagRow_p(), interval_p(), observationId_p(), processorId_p(), scanNumber_p(), sigma_p(), stateId_p(), time_p(), timeCentroid_p(), uvw_p(), weight_p(), antenna3_p(), baselineRef_p(), data_p(), feed3_p(), floatData_p(), lagData_p(), phaseId_p(), pulsarBin_p(), pulsarGateId_p(), sigmaSpectrum_p(), timeExtraPrec_p(), uvw2_p(), videoPoint_p(), weightSpectrum_p(), weightSpectrumCorrected_p(), correctedData_p(), imagingWeight_p(), modelData_p(), timeMeas_p(), timeCentroidMeas_p(), uvwMeas_p(), uvw2Meas_p(), exposureQuant_p(), intervalQuant_p(), timeQuant_p(), timeCentroidQuant_p(), uvwQuant_p(), timeExtraPrecQuant_p(), uvw2Quant_p() { } void MSMainColumns::attach(MeasurementSet& ms) { ROMSMainColumns::attach(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)); attachOptionalCols(ms); } void MSMainColumns::attachOptionalCols(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)); } } // Local Variables: // compile-command: "gmake MSMainColumns" // End: } //# NAMESPACE CASACORE - END casacore-2.4.1/ms/MeasurementSets/MSMainColumns.h000066400000000000000000000575771321422335000217170ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef MS_MSMAINCOLUMNS_H #define MS_MSMAINCOLUMNS_H #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN class MeasurementSet; class String; template class Vector; // // A class for easy read-only access to MeasurementSet main table columns // // // // // //
      • MeasurementSet //
      • ArrayColumn //
      • ScalarColumn // // // // ROMSColumns stands for Read-Only MeasurementSet Table columns. // // // // This class provides read-only 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"); // ROMSColumns msc(ms); // // show data from row 5 // cout << msc.data()(5); // // show name of antenna on row 3 in antenna table // cout << msc.antenna().name(); // // // // // See MSColumns for the motivation. // // // //
      • We might decide to merge all the MSColumn classes with the // corresponding MeasurementSet classes. // class ROMSMainColumns { public: // Create a columns object that accesses the data in the specified Table ROMSMainColumns(const MeasurementSet& ms); // The desctructor does nothing special ~ROMSMainColumns(); // Access to required columns // const ROScalarColumn& antenna1() const {return antenna1_p;} const ROScalarColumn& antenna2() const {return antenna2_p;} const ROScalarColumn& arrayId() const {return arrayId_p;} const ROScalarColumn& dataDescId() const {return dataDescId_p;} const ROScalarColumn& exposure() const {return exposure_p;} const ROScalarQuantColumn& exposureQuant() const { return exposureQuant_p;} const ROScalarColumn& feed1() const {return feed1_p;} const ROScalarColumn& feed2() const {return feed2_p;} const ROScalarColumn& fieldId() const {return fieldId_p;} const ROArrayColumn& flag() const {return flag_p;} const ROArrayColumn& flagCategory() const {return flagCategory_p;} const ROScalarColumn& flagRow() const {return flagRow_p;} const ROScalarColumn& interval() const {return interval_p;} const ROScalarQuantColumn& intervalQuant() const { return intervalQuant_p;} const ROScalarColumn& observationId() const {return observationId_p;} const ROScalarColumn& processorId() const {return processorId_p;} const ROScalarColumn& scanNumber() const {return scanNumber_p;} const ROArrayColumn& sigma() const {return sigma_p;} const ROScalarColumn& stateId() const {return stateId_p;} const ROScalarColumn& time() const {return time_p;} const ROScalarQuantColumn& timeQuant() const { return timeQuant_p;} const ROScalarMeasColumn& timeMeas() const { return timeMeas_p;} const ROScalarColumn& timeCentroid() const {return timeCentroid_p;} const ROScalarQuantColumn& timeCentroidQuant() const { return timeCentroidQuant_p;} const ROScalarMeasColumn& timeCentroidMeas() const { return timeCentroidMeas_p;} const ROArrayColumn& uvw() const {return uvw_p;} const ROArrayQuantColumn& uvwQuant() const { return uvwQuant_p;} const ROScalarMeasColumn& uvwMeas() const { return uvwMeas_p;} const ROArrayColumn& weight() const {return weight_p;} // // Access to optional columns // const ROScalarColumn& antenna3() const {return antenna3_p;} const ROScalarColumn& baselineRef() const {return baselineRef_p;} const ROArrayColumn& correctedData() const {return correctedData_p;} const ROArrayColumn& data() const {return data_p;} const ROScalarColumn& feed3() const {return feed3_p;} const ROArrayColumn& floatData() const {return floatData_p;} const ROArrayColumn& imagingWeight() const {return imagingWeight_p;} const ROArrayColumn& lagData() const {return lagData_p;} const ROArrayColumn& modelData() const {return modelData_p;} const ROScalarColumn& phaseId() const {return phaseId_p;} const ROScalarColumn& pulsarBin() const {return pulsarBin_p;} const ROScalarColumn& pulsarGateId() const {return pulsarGateId_p;} const ROArrayColumn& sigmaSpectrum() const {return sigmaSpectrum_p;} const ROScalarColumn& timeExtraPrec() const {return timeExtraPrec_p;} const ROScalarQuantColumn& timeExtraPrecQuant() const { return timeExtraPrecQuant_p;} const ROArrayColumn& uvw2() const {return uvw2_p;} const ROScalarMeasColumn& uvw2Meas() const { return uvw2Meas_p;} const ROArrayQuantColumn& uvw2Quant() const { return uvw2Quant_p;} const ROArrayColumn& videoPoint() const {return videoPoint_p;} const ROArrayColumn& weightSpectrum() const {return weightSpectrum_p;} const ROArrayColumn& weightSpectrumCorrected() const {return weightSpectrumCorrected_p;} // // Convenience function that returns the number of rows in any of the columns uInt nrow() const {return antenna1_p.nrow();} // Returns the category labels for the FLAG_CATEGORY column. Vector flagCategories() const; protected: //# default constructor creates a object that is not usable. Use the attach //# function correct this. ROMSMainColumns(); //# 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. ROMSMainColumns(const ROMSMainColumns&); ROMSMainColumns& operator=(const ROMSMainColumns&); //# Check if any optional columns exist and if so attach them. void attachOptionalCols(const MeasurementSet& ms); //# required columns ROScalarColumn antenna1_p; ROScalarColumn antenna2_p; ROScalarColumn arrayId_p; ROScalarColumn dataDescId_p; ROScalarColumn exposure_p; ROScalarColumn feed1_p; ROScalarColumn feed2_p; ROScalarColumn fieldId_p; ROArrayColumn flag_p; ROArrayColumn flagCategory_p; ROScalarColumn flagRow_p; ROScalarColumn interval_p; ROScalarColumn observationId_p; ROScalarColumn processorId_p; ROScalarColumn scanNumber_p; ROArrayColumn sigma_p; ROScalarColumn stateId_p; ROScalarColumn time_p; ROScalarColumn timeCentroid_p; ROArrayColumn uvw_p; ROArrayColumn weight_p; //# optional columns ROScalarColumn antenna3_p; ROScalarColumn baselineRef_p; ROArrayColumn data_p; ROScalarColumn feed3_p; ROArrayColumn floatData_p; ROArrayColumn lagData_p; ROScalarColumn phaseId_p; ROScalarColumn pulsarBin_p; ROScalarColumn pulsarGateId_p; ROArrayColumn sigmaSpectrum_p; ROScalarColumn timeExtraPrec_p; ROArrayColumn uvw2_p; ROArrayColumn videoPoint_p; ROArrayColumn weightSpectrum_p; ROArrayColumn weightSpectrumCorrected_p; //# columns required for synthesis applications - all optional ROArrayColumn correctedData_p; ROArrayColumn imagingWeight_p; ROArrayColumn modelData_p; //# Access to Measure columns ROScalarMeasColumn timeMeas_p; ROScalarMeasColumn timeCentroidMeas_p; ROScalarMeasColumn uvwMeas_p; //# optional Measure columns ROScalarMeasColumn uvw2Meas_p; //# Access to Quantum columns ROScalarQuantColumn exposureQuant_p; ROScalarQuantColumn intervalQuant_p; ROScalarQuantColumn timeQuant_p; ROScalarQuantColumn timeCentroidQuant_p; ROArrayQuantColumn uvwQuant_p; //# optional Quantum columns ROScalarQuantColumn timeExtraPrecQuant_p; ROArrayQuantColumn uvw2Quant_p; }; // // A class for easy read-write 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 ROMSMainColumns { public: // Create a columns object that accesses the data in the specified Table MSMainColumns(MeasurementSet& ms); // The desctructor does nothing special ~MSMainColumns(); // Read-write 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;} // // Read-write 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;} // // Read-only access to required columns // const ROScalarColumn& antenna1() const { return ROMSMainColumns::antenna1();} const ROScalarColumn& antenna2() const { return ROMSMainColumns::antenna2();} const ROScalarColumn& arrayId() const { return ROMSMainColumns::arrayId();} const ROScalarColumn& dataDescId() const { return ROMSMainColumns::dataDescId();} const ROScalarColumn& exposure() const { return ROMSMainColumns::exposure();} const ROScalarQuantColumn& exposureQuant() const { return ROMSMainColumns::exposureQuant();} const ROScalarColumn& feed1() const { return ROMSMainColumns::feed1();} const ROScalarColumn& feed2() const { return ROMSMainColumns::feed2();} const ROScalarColumn& fieldId() const { return ROMSMainColumns::fieldId();} const ROArrayColumn& flag() const { return ROMSMainColumns::flag();} const ROArrayColumn& flagCategory() const { return ROMSMainColumns::flagCategory();} const ROScalarColumn& flagRow() const { return ROMSMainColumns::flagRow();} const ROScalarColumn& interval() const { return ROMSMainColumns::interval();} const ROScalarQuantColumn& intervalQuant() const { return ROMSMainColumns::intervalQuant();} const ROScalarColumn& observationId() const { return ROMSMainColumns::observationId();} const ROScalarColumn& processorId() const { return ROMSMainColumns::processorId();} const ROScalarColumn& scanNumber() const { return ROMSMainColumns::scanNumber();} const ROArrayColumn& sigma() const { return ROMSMainColumns::sigma();} const ROScalarColumn& stateId() const { return ROMSMainColumns::stateId();} const ROScalarColumn& time() const { return ROMSMainColumns::time();} const ROScalarQuantColumn& timeQuant() const { return ROMSMainColumns::timeQuant();} const ROScalarMeasColumn& timeMeas() const { return ROMSMainColumns::timeMeas();} const ROScalarColumn& timeCentroid() const { return ROMSMainColumns::timeCentroid();} const ROScalarQuantColumn& timeCentroidQuant() const { return ROMSMainColumns::timeCentroidQuant();} const ROScalarMeasColumn& timeCentroidMeas() const { return ROMSMainColumns::timeCentroidMeas();} const ROArrayColumn& uvw() const { return ROMSMainColumns::uvw();} const ROArrayQuantColumn& uvwQuant() const { return ROMSMainColumns::uvwQuant();} const ROScalarMeasColumn& uvwMeas() const { return ROMSMainColumns::uvwMeas();} const ROArrayColumn& weight() const { return ROMSMainColumns::weight();} // // Read-only access to optional columns // const ROScalarColumn& antenna3() const { return ROMSMainColumns::antenna3();} const ROScalarColumn& baselineRef() const { return ROMSMainColumns::baselineRef();} const ROArrayColumn& correctedData() const { return ROMSMainColumns::correctedData();} const ROArrayColumn& data() const { return ROMSMainColumns::data();} const ROScalarColumn& feed3() const { return ROMSMainColumns::feed3();} const ROArrayColumn& floatData() const { return ROMSMainColumns::floatData();} const ROArrayColumn& imagingWeight() const { return ROMSMainColumns::imagingWeight();} const ROArrayColumn& lagData() const { return ROMSMainColumns::lagData();} const ROArrayColumn& modelData() const { return ROMSMainColumns::modelData();} const ROScalarColumn& phaseId() const { return ROMSMainColumns::phaseId();} const ROScalarColumn& pulsarBin() const { return ROMSMainColumns::pulsarBin();} const ROScalarColumn& pulsarGateId() const { return ROMSMainColumns::pulsarGateId();} const ROArrayColumn& sigmaSpectrum() const { return ROMSMainColumns::sigmaSpectrum();} const ROScalarColumn& timeExtraPrec() const { return ROMSMainColumns::timeExtraPrec();} const ROScalarQuantColumn& timeExtraPrecQuant() const { return ROMSMainColumns::timeExtraPrecQuant();} const ROArrayColumn& uvw2() const { return ROMSMainColumns::uvw2();} const ROScalarMeasColumn& uvw2Meas() const { return ROMSMainColumns::uvw2Meas();} const ROArrayQuantColumn& uvw2Quant() const { return ROMSMainColumns::uvw2Quant();} const ROArrayColumn& videoPoint() const { return ROMSMainColumns::videoPoint();} const ROArrayColumn& weightSpectrum() const { return ROMSMainColumns::weightSpectrum();} const ROArrayColumn& weightSpectrumCorrected() const { return ROMSMainColumns::weightSpectrumCorrected();} // // 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(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(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; }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/ms/MeasurementSets/MSMainEnums.h000066400000000000000000000231621321422335000213450ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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-2.4.1/ms/MeasurementSets/MSObsColumns.cc000066400000000000000000000171451321422335000216770ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN ROMSObservationColumns:: ROMSObservationColumns(const MSObservation& msObservation): flagRow_p(msObservation, MSObservation:: columnName(MSObservation::FLAG_ROW)), log_p(msObservation, MSObservation::columnName(MSObservation::LOG)), observer_p(msObservation, MSObservation:: columnName(MSObservation::OBSERVER)), project_p(msObservation, MSObservation:: columnName(MSObservation::PROJECT)), releaseDate_p(msObservation, MSObservation:: columnName(MSObservation::RELEASE_DATE)), schedule_p(msObservation, MSObservation:: columnName(MSObservation::SCHEDULE)), scheduleType_p(msObservation, MSObservation:: columnName(MSObservation::SCHEDULE_TYPE)), telescopeName_p(msObservation, MSObservation:: columnName(MSObservation::TELESCOPE_NAME)), timeRange_p(msObservation, MSObservation:: columnName(MSObservation::TIME_RANGE)), releaseDateMeas_p(msObservation, MSObservation:: columnName(MSObservation::RELEASE_DATE)), timeRangeMeas_p(msObservation, MSObservation:: columnName(MSObservation::TIME_RANGE)), releaseDateQuant_p(msObservation, MSObservation:: columnName(MSObservation::RELEASE_DATE)), timeRangeQuant_p(msObservation, MSObservation:: columnName(MSObservation::TIME_RANGE)) {} ROMSObservationColumns::~ROMSObservationColumns() {} ROMSObservationColumns::ROMSObservationColumns(): flagRow_p(), log_p(), observer_p(), project_p(), releaseDate_p(), schedule_p(), scheduleType_p(), telescopeName_p(), timeRange_p(), releaseDateMeas_p(), timeRangeMeas_p(), releaseDateQuant_p(), timeRangeQuant_p() {} void ROMSObservationColumns::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)); } MSObservationColumns:: MSObservationColumns(MSObservation& msObservation): ROMSObservationColumns(msObservation), flagRow_p(msObservation, MSObservation:: columnName(MSObservation::FLAG_ROW)), log_p(msObservation, MSObservation::columnName(MSObservation::LOG)), observer_p(msObservation, MSObservation:: columnName(MSObservation::OBSERVER)), project_p(msObservation, MSObservation:: columnName(MSObservation::PROJECT)), releaseDate_p(msObservation, MSObservation:: columnName(MSObservation::RELEASE_DATE)), schedule_p(msObservation, MSObservation:: columnName(MSObservation::SCHEDULE)), scheduleType_p(msObservation, MSObservation:: columnName(MSObservation::SCHEDULE_TYPE)), telescopeName_p(msObservation, MSObservation:: columnName(MSObservation::TELESCOPE_NAME)), timeRange_p(msObservation, MSObservation:: columnName(MSObservation::TIME_RANGE)), releaseDateMeas_p(msObservation, MSObservation:: columnName(MSObservation::RELEASE_DATE)), timeRangeMeas_p(msObservation, MSObservation:: columnName(MSObservation::TIME_RANGE)), releaseDateQuant_p(msObservation, MSObservation:: columnName(MSObservation::RELEASE_DATE)), timeRangeQuant_p(msObservation, MSObservation:: columnName(MSObservation::TIME_RANGE)) {} MSObservationColumns::~MSObservationColumns() {} void MSObservationColumns:: setEpochRef(MEpoch::Types ref, Bool tableMustBeEmpty) { timeRangeMeas_p.setDescRefCode(ref, tableMustBeEmpty); releaseDateMeas_p.setDescRefCode(ref, tableMustBeEmpty); } MSObservationColumns::MSObservationColumns(): ROMSObservationColumns(), flagRow_p(), log_p(), observer_p(), project_p(), releaseDate_p(), schedule_p(), scheduleType_p(), telescopeName_p(), timeRange_p(), releaseDateMeas_p(), timeRangeMeas_p(), releaseDateQuant_p(), timeRangeQuant_p() {} void MSObservationColumns::attach(MSObservation& msObservation) { ROMSObservationColumns::attach(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)); } // Local Variables: // compile-command: "gmake MSObsColumns" // End: } //# NAMESPACE CASACORE - END casacore-2.4.1/ms/MeasurementSets/MSObsColumns.h000066400000000000000000000251321321422335000215340ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 read-only access to MSObservation columns // // // // // //
      • MSObservation //
      • ArrayColumn //
      • ScalarColumn // // // // ROMSObservationColumns stands for Read-Only MeasurementSet Observation // Table columns. // // // // This class provides read-only 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 // ROMSColumns for an example. // // // // See MSColumns for the motivation. // class ROMSObservationColumns { public: // Create a columns object that accesses the data in the specified Table ROMSObservationColumns(const MSObservation& msObservation); // The destructor does nothing special ~ROMSObservationColumns(); // Access to required columns // const ROScalarColumn& flagRow() const {return flagRow_p;} const ROArrayColumn& log() const {return log_p;} const ROScalarColumn& observer() const {return observer_p;} const ROScalarColumn& project() const {return project_p;} const ROScalarColumn& releaseDate() const {return releaseDate_p;} const ROScalarQuantColumn& releaseDateQuant() const { return releaseDateQuant_p;} const ROScalarMeasColumn& releaseDateMeas() const { return releaseDateMeas_p;} const ROArrayColumn& schedule() const {return schedule_p;} const ROScalarColumn& scheduleType() const {return scheduleType_p;} const ROScalarColumn& telescopeName() const {return telescopeName_p;} const ROArrayColumn& timeRange() const {return timeRange_p;} const ROArrayQuantColumn& timeRangeQuant() const { return timeRangeQuant_p;} const ROArrayMeasColumn& timeRangeMeas() const { return timeRangeMeas_p;} // // Convenience function that returns the number of rows in any of the columns uInt nrow() const {return flagRow_p.nrow();} protected: //# default constructor creates a object that is not usable. Use the attach //# function correct this. ROMSObservationColumns(); //# 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. ROMSObservationColumns(const ROMSObservationColumns&); ROMSObservationColumns& operator=(const ROMSObservationColumns&); //# required columns ROScalarColumn flagRow_p; ROArrayColumn log_p; ROScalarColumn observer_p; ROScalarColumn project_p; ROScalarColumn releaseDate_p; ROArrayColumn schedule_p; ROScalarColumn scheduleType_p; ROScalarColumn telescopeName_p; ROArrayColumn timeRange_p; //# Access to Measure columns ROScalarMeasColumn releaseDateMeas_p; ROArrayMeasColumn timeRangeMeas_p; //# Access to Quantum columns ROScalarQuantColumn releaseDateQuant_p; ROArrayQuantColumn timeRangeQuant_p; }; // // A class to provide easy read-write 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 ROMSObservationColumns { public: // Create a columns object that accesses the data in the specified Table MSObservationColumns(MSObservation& msObservation); // The desctructor does nothing special ~MSObservationColumns(); // Read-write 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;} // // Read-only access to required columns // const ROScalarColumn& flagRow() const { return ROMSObservationColumns::flagRow();} const ROArrayColumn& log() const { return ROMSObservationColumns::log();} const ROScalarColumn& observer() const { return ROMSObservationColumns::observer();} const ROScalarColumn& project() const { return ROMSObservationColumns::project();} const ROScalarColumn& releaseDate() const { return ROMSObservationColumns::releaseDate();} const ROScalarQuantColumn& releaseDateQuant() const { return ROMSObservationColumns::releaseDateQuant();} const ROScalarMeasColumn& releaseDateMeas() const { return ROMSObservationColumns::releaseDateMeas();} const ROArrayColumn& schedule() const { return ROMSObservationColumns::schedule();} const ROScalarColumn& scheduleType() const { return ROMSObservationColumns::scheduleType();} const ROScalarColumn& telescopeName() const { return ROMSObservationColumns::telescopeName();} const ROArrayColumn& timeRange() const { return ROMSObservationColumns::timeRange();} const ROArrayQuantColumn& timeRangeQuant() const { return ROMSObservationColumns::timeRangeQuant();} const ROArrayMeasColumn& timeRangeMeas() const { return ROMSObservationColumns::timeRangeMeas();} // // 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(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; }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/ms/MeasurementSets/MSObsEnums.h000066400000000000000000000070031321422335000212000ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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-2.4.1/ms/MeasurementSets/MSObservation.cc000066400000000000000000000146311321422335000221030ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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, uInt 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 &, uInt, 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; } void MSObservation::init() { if (! columnMap_p.ndefined()) { // the PredefinedColumns // FLAG_ROW colMapDef(FLAG_ROW,"FLAG_ROW",TpBool, "Row flag","",""); // LOG colMapDef(LOG,"LOG",TpArrayString, "Observing log","",""); // OBSERVER colMapDef(OBSERVER, "OBSERVER", TpString, "Name of observer(s)","",""); // PROJECT colMapDef(PROJECT,"PROJECT",TpString, "Project identification string","",""); // RELEASE_DATE colMapDef(RELEASE_DATE,"RELEASE_DATE",TpDouble, "Release date when data becomes public","s","Epoch"); // SCHEDULE colMapDef(SCHEDULE,"SCHEDULE",TpArrayString, "Observing schedule","",""); // SCHEDULE_TYPE colMapDef(SCHEDULE_TYPE,"SCHEDULE_TYPE",TpString, "Observing schedule type","",""); // TELESCOPE_NAME colMapDef(TELESCOPE_NAME,"TELESCOPE_NAME",TpString, "Telescope Name (e.g. WSRT, VLBA)"); // TIME_RANGE colMapDef(TIME_RANGE,"TIME_RANGE",TpArrayDouble, "Start and end of observation","s","Epoch"); // PredefinedKeywords // init requiredTableDesc TableDesc requiredTD; // all required keywords // Define the columns with fixed size arrays IPosition shape(1,2); ColumnDesc::Option option=ColumnDesc::Direct; addColumnToDesc(requiredTD, TIME_RANGE, shape, option); // Define the columns with known dimensionality addColumnToDesc(requiredTD, LOG, 1); addColumnToDesc(requiredTD, SCHEDULE, 1); for (Int i = UNDEFINED_KEYWORD+1; i <= NUMBER_PREDEFINED_KEYWORDS; i++) { addKeyToDesc(requiredTD, PredefinedKeywords(i)); } // all required columns for (Int i = UNDEFINED_COLUMN+1; i <= NUMBER_REQUIRED_COLUMNS; i++) { addColumnToDesc(requiredTD, PredefinedColumns(i)); } requiredTD_p=new TableDesc(requiredTD); } } MSObservation MSObservation::referenceCopy(const String& newTableName, const Block& writableColumns) const { return MSObservation(MSTable::referenceCopy (newTableName,writableColumns)); } } //# NAMESPACE CASACORE - END casacore-2.4.1/ms/MeasurementSets/MSObservation.h000066400000000000000000000117531321422335000217470ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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, uInt 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 void init(); private: // required by the need to throw an exception in the destructor Bool hasBeenDestroyed_p; }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/ms/MeasurementSets/MSPointing.cc000066400000000000000000000155161321422335000214020ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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, uInt 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 &, uInt, 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; } void MSPointing::init() { if (! columnMap_p.ndefined()) { // the PredefinedColumns // ANTENNA_ID colMapDef(ANTENNA_ID, "ANTENNA_ID", TpInt, "Antenna Id","",""); // DIRECTION colMapDef(DIRECTION, "DIRECTION", TpArrayDouble, "Antenna pointing direction as polynomial in time","rad" ,"Direction"); // INTERVAL colMapDef(INTERVAL, "INTERVAL", TpDouble, "Time interval","s",""); // NAME colMapDef(NAME, "NAME", TpString, "Pointing position name","",""); // NUM_POLY colMapDef(NUM_POLY, "NUM_POLY", TpInt, "Series order","",""); // TARGET colMapDef(TARGET, "TARGET", TpArrayDouble, "target direction as polynomial in time","rad" ,"Direction"); // TIME colMapDef(TIME, "TIME", TpDouble, "Time interval midpoint","s","Epoch"); // TIME_ORIGIN colMapDef(TIME_ORIGIN, "TIME_ORIGIN", TpDouble, "Time origin for direction","s","Epoch"); // TRACKING colMapDef(TRACKING, "TRACKING", TpBool, "Tracking flag - True if on position","",""); // ENCODER colMapDef(ENCODER, "ENCODER", TpArrayDouble, "Encoder values","rad","Direction"); // ON_SOURCE colMapDef(ON_SOURCE, "ON_SOURCE", TpBool, "On source flag","",""); // OVER_THE_TOP colMapDef(OVER_THE_TOP, "OVER_THE_TOP", TpBool, "Antenna over the top","",""); // POINTING_MODEL_ID colMapDef(POINTING_MODEL_ID,"POINTING_MODEL_ID",TpInt, "Pointing model id","",""); // POINTING_OFFSET colMapDef(POINTING_OFFSET, "POINTING_OFFSET", TpArrayDouble, "A priori pointing correction as polynomial in time", "rad","Direction"); // SOURCE_OFFSET colMapDef(SOURCE_OFFSET, "SOURCE_OFFSET", TpArrayDouble, "Offset from source as polynomial in time","rad","Direction"); // PredefinedKeywords // init requiredTableDesc TableDesc requiredTD; // all required keywords // First define the columns with known dimensionality addColumnToDesc(requiredTD, DIRECTION, 2); uInt i; for (i = UNDEFINED_KEYWORD+1; i <= NUMBER_PREDEFINED_KEYWORDS; i++) { addKeyToDesc(requiredTD, PredefinedKeywords(i)); } // all required columns // Now define all other columns (duplicates are skipped) for (i = UNDEFINED_COLUMN+1; i <= NUMBER_REQUIRED_COLUMNS; i++) { addColumnToDesc(requiredTD, PredefinedColumns(i)); } requiredTD_p=new TableDesc(requiredTD); } } MSPointing MSPointing::referenceCopy(const String& newTableName, const Block& writableColumns) const { return MSPointing(MSTable:: referenceCopy(newTableName,writableColumns)); } } //# NAMESPACE CASACORE - END casacore-2.4.1/ms/MeasurementSets/MSPointing.h000066400000000000000000000115711321422335000212410ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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, uInt 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 void init(); private: // required by the need to throw an exception in the destructor Bool hasBeenDestroyed_p; }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/ms/MeasurementSets/MSPointingColumns.cc000066400000000000000000000344411321422335000227410ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN ROMSPointingColumns:: ROMSPointingColumns(const MSPointing& msPointing): antennaId_p(msPointing, MSPointing:: columnName(MSPointing::ANTENNA_ID)), direction_p(msPointing, MSPointing::columnName(MSPointing::DIRECTION)), interval_p(msPointing, MSPointing::columnName(MSPointing::INTERVAL)), name_p(msPointing, MSPointing::columnName(MSPointing::NAME)), numPoly_p(msPointing, MSPointing::columnName(MSPointing::NUM_POLY)), target_p(msPointing, MSPointing::columnName(MSPointing::TARGET)), time_p(msPointing, MSPointing::columnName(MSPointing::TIME)), timeOrigin_p(msPointing, MSPointing:: columnName(MSPointing::TIME_ORIGIN)), tracking_p(msPointing, MSPointing::columnName(MSPointing::TRACKING)), encoder_p(), onSource_p(), pointingModelId_p(), pointingOffset_p(), sourceOffset_p(), overTheTop_p(), directionMeas_p(msPointing, MSPointing:: columnName(MSPointing::DIRECTION)), targetMeas_p(msPointing, MSPointing::columnName(MSPointing::TARGET)), timeMeas_p(msPointing, MSPointing::columnName(MSPointing::TIME)), timeOriginMeas_p(msPointing, MSPointing:: columnName(MSPointing::TIME_ORIGIN)), encoderMeas_p(), pointingOffsetMeas_p(), sourceOffsetMeas_p(), intervalQuant_p(msPointing, MSPointing:: columnName(MSPointing::INTERVAL)), timeQuant_p(msPointing, MSPointing::columnName(MSPointing::TIME)), timeOriginQuant_p(msPointing, MSPointing:: columnName(MSPointing::TIME_ORIGIN)) { attachOptionalCols(msPointing); } ROMSPointingColumns::~ROMSPointingColumns() {} MDirection ROMSPointingColumns::directionMeas(Int row, Double interTime) const { return MSFieldColumns::interpolateDirMeas(directionMeasCol()(row), numPoly()(row), interTime, time()(row)); } MDirection ROMSPointingColumns::targetMeas(Int row, Double interTime) const { return MSFieldColumns::interpolateDirMeas(targetMeasCol()(row), numPoly()(row), interTime, time()(row)); } MDirection ROMSPointingColumns::pointingOffsetMeas(Int row, Double interTime) const { if (pointingOffsetMeasCol().isNull()) return MDirection(); return MSFieldColumns::interpolateDirMeas(pointingOffsetMeasCol()(row), numPoly()(row), interTime, time()(row)); } MDirection ROMSPointingColumns::sourceOffsetMeas(Int row, Double interTime) const { if (sourceOffsetMeasCol().isNull()) return MDirection(); return MSFieldColumns::interpolateDirMeas(sourceOffsetMeasCol()(row), numPoly()(row), interTime, time()(row)); } Int ROMSPointingColumns::pointingIndex(Int antenna, Double ptime, Int guessRow) const { if((this->nrow()) < 1) return -1; // return the first row matching the requirements const Int 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){ Int start=guessRow; Int end=nrow; if(k==1){ start=0; end=guessRow; } for (Int 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; } ROMSPointingColumns::ROMSPointingColumns(): antennaId_p(), direction_p(), interval_p(), name_p(), numPoly_p(), target_p(), time_p(), timeOrigin_p(), tracking_p(), encoder_p(), onSource_p(), pointingModelId_p(), pointingOffset_p(), sourceOffset_p(), overTheTop_p(), directionMeas_p(), targetMeas_p(), timeMeas_p(), timeOriginMeas_p(), encoderMeas_p(), pointingOffsetMeas_p(), sourceOffsetMeas_p(), intervalQuant_p(), timeQuant_p(), timeOriginQuant_p() { } void ROMSPointingColumns::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 ROMSPointingColumns:: 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); } MSPointingColumns::MSPointingColumns(MSPointing& msPointing): ROMSPointingColumns(msPointing), antennaId_p(msPointing, MSPointing:: columnName(MSPointing::ANTENNA_ID)), direction_p(msPointing, MSPointing::columnName(MSPointing::DIRECTION)), interval_p(msPointing, MSPointing::columnName(MSPointing::INTERVAL)), name_p(msPointing, MSPointing::columnName(MSPointing::NAME)), numPoly_p(msPointing, MSPointing::columnName(MSPointing::NUM_POLY)), target_p(msPointing, MSPointing::columnName(MSPointing::TARGET)), time_p(msPointing, MSPointing::columnName(MSPointing::TIME)), timeOrigin_p(msPointing, MSPointing:: columnName(MSPointing::TIME_ORIGIN)), tracking_p(msPointing, MSPointing::columnName(MSPointing::TRACKING)), encoder_p(), onSource_p(), pointingModelId_p(), pointingOffset_p(), sourceOffset_p(), overTheTop_p(), directionMeas_p(msPointing, MSPointing:: columnName(MSPointing::DIRECTION)), targetMeas_p(msPointing, MSPointing::columnName(MSPointing::TARGET)), timeMeas_p(msPointing, MSPointing::columnName(MSPointing::TIME)), timeOriginMeas_p(msPointing, MSPointing:: columnName(MSPointing::TIME_ORIGIN)), encoderMeas_p(), pointingOffsetMeas_p(), sourceOffsetMeas_p(), intervalQuant_p(msPointing, MSPointing:: columnName(MSPointing::INTERVAL)), timeQuant_p(msPointing, MSPointing::columnName(MSPointing::TIME)), timeOriginQuant_p(msPointing, MSPointing:: columnName(MSPointing::TIME_ORIGIN)) { attachOptionalCols(msPointing); } MSPointingColumns::~MSPointingColumns() {} MSPointingColumns::MSPointingColumns(): ROMSPointingColumns(), antennaId_p(), direction_p(), interval_p(), name_p(), numPoly_p(), target_p(), time_p(), timeOrigin_p(), tracking_p(), encoder_p(), onSource_p(), pointingModelId_p(), pointingOffset_p(), sourceOffset_p(), overTheTop_p(), directionMeas_p(), targetMeas_p(), timeMeas_p(), timeOriginMeas_p(), encoderMeas_p(), pointingOffsetMeas_p(), sourceOffsetMeas_p(), intervalQuant_p(), timeQuant_p(), timeOriginQuant_p() { } void MSPointingColumns::attach(MSPointing& msPointing) { ROMSPointingColumns::attach(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(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); } 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); } } // Local Variables: // compile-command: "gmake MSPointingColumns" // End: } //# NAMESPACE CASACORE - END casacore-2.4.1/ms/MeasurementSets/MSPointingColumns.h000066400000000000000000000413631321422335000226040ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 read-only access to MSPointing columns // // // // // //
      • MSPointing //
      • ArrayColumn //
      • ScalarColumn // // // // ROMSPointingColumns stands for Read-Only MeasurementSet Pointing Table columns. // // // // This class provides read-only 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 // ROMSColumns for an example. // // // // See MSColumns for the motivation. // class ROMSPointingColumns { public: // Create a columns object that accesses the data in the specified Table ROMSPointingColumns(const MSPointing& msPointing); // The destructor does nothing special ~ROMSPointingColumns(); // Access to required columns // const ROScalarColumn& antennaId() const {return antennaId_p;} const ROScalarColumn& time() const {return time_p;} const ROScalarQuantColumn& timeQuant() const {return timeQuant_p;} const ROScalarMeasColumn& timeMeas() const {return timeMeas_p;} const ROScalarColumn& interval() const {return interval_p;} const ROScalarQuantColumn& intervalQuant() const { return intervalQuant_p;} const ROScalarColumn& name() const {return name_p;} const ROScalarColumn& numPoly() const {return numPoly_p;} const ROScalarColumn& timeOrigin() const {return timeOrigin_p;} const ROScalarQuantColumn& timeOriginQuant() const { return timeOriginQuant_p;} const ROScalarMeasColumn& timeOriginMeas() const { return timeOriginMeas_p;} const ROArrayColumn& direction() const {return direction_p;} const ROArrayMeasColumn& directionMeasCol() const { return directionMeas_p;} const ROArrayColumn& target() const {return target_p;} const ROArrayMeasColumn& targetMeasCol()const { return targetMeas_p;} const ROScalarColumn& tracking() const {return tracking_p;} // // Access to optional columns // const ROArrayColumn& pointingOffset() const { return pointingOffset_p;} const ROArrayMeasColumn& pointingOffsetMeasCol() const { return pointingOffsetMeas_p;} const ROArrayColumn& sourceOffset() const {return sourceOffset_p;} const ROArrayMeasColumn& sourceOffsetMeasCol() const { return sourceOffsetMeas_p;} const ROArrayColumn& encoder() const {return encoder_p;} const ROScalarMeasColumn& encoderMeas() const { return encoderMeas_p;} const ROScalarColumn& pointingModelId() const { return pointingModelId_p;} const ROScalarColumn& onSource() const {return onSource_p;} const ROScalarColumn& 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(Int row, Double time = 0) const; MDirection targetMeas(Int row, Double time = 0) const; MDirection pointingOffsetMeas(Int row, Double time = 0) const; MDirection sourceOffsetMeas(Int 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. Int pointingIndex(Int antenna, Double time, Int guessRow=0) const; // Convenience function that returns the number of rows in any of the columns uInt nrow() const {return antennaId_p.nrow();} protected: //# default constructor creates a object that is not usable. Use the attach //# function correct this. ROMSPointingColumns(); //# 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. ROMSPointingColumns(const ROMSPointingColumns&); ROMSPointingColumns& operator=(const ROMSPointingColumns&); //# Check if any optional columns exist and if so attach them. void attachOptionalCols(const MSPointing& msPointing); //# required columns ROScalarColumn antennaId_p; ROArrayColumn direction_p; ROScalarColumn interval_p; ROScalarColumn name_p; ROScalarColumn numPoly_p; ROArrayColumn target_p; ROScalarColumn time_p; ROScalarColumn timeOrigin_p; ROScalarColumn tracking_p; //# optional columns ROArrayColumn encoder_p; ROScalarColumn onSource_p; ROScalarColumn pointingModelId_p; ROArrayColumn pointingOffset_p; ROArrayColumn sourceOffset_p; ROScalarColumn overTheTop_p; //# Access to Measure columns ROArrayMeasColumn directionMeas_p; ROArrayMeasColumn targetMeas_p; ROScalarMeasColumn timeMeas_p; ROScalarMeasColumn timeOriginMeas_p; //# optional Measure columns ROScalarMeasColumn encoderMeas_p; ROArrayMeasColumn pointingOffsetMeas_p; ROArrayMeasColumn sourceOffsetMeas_p; //# Access to Quantum columns ROScalarQuantColumn intervalQuant_p; ROScalarQuantColumn timeQuant_p; ROScalarQuantColumn timeOriginQuant_p; }; // // A class to provide easy read-write 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 ROMSPointingColumns { public: // Construct from the supplied Table MSPointingColumns(MSPointing& msPointing); // The destructor does nothing special ~MSPointingColumns(); // Read-write 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. These functions are in the ROMSPointingColumns class. // 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;} // // Read-write 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. These functions are in the ROMSPointingColumns class. // 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;} // // Read-only access to required columns // const ROScalarColumn& antennaId() const { return ROMSPointingColumns::antennaId();} const ROScalarColumn& time() const { return ROMSPointingColumns::time();} const ROScalarQuantColumn& timeQuant() const { return ROMSPointingColumns::timeQuant();} const ROScalarMeasColumn& timeMeas() const { return ROMSPointingColumns::timeMeas();} const ROScalarColumn& interval() const { return ROMSPointingColumns::interval();} const ROScalarQuantColumn& intervalQuant() const { return ROMSPointingColumns::intervalQuant();} const ROScalarColumn& name() const { return ROMSPointingColumns::name();} const ROScalarColumn& numPoly() const { return ROMSPointingColumns::numPoly();} const ROScalarColumn& timeOrigin() const { return ROMSPointingColumns::timeOrigin();} const ROScalarQuantColumn& timeOriginQuant() const { return ROMSPointingColumns::timeOriginQuant();} const ROScalarMeasColumn& timeOriginMeas() const { return ROMSPointingColumns::timeOriginMeas();} const ROArrayColumn& direction() const { return ROMSPointingColumns::direction();} const ROArrayMeasColumn& directionMeasCol() const { return ROMSPointingColumns::directionMeasCol();} const ROArrayColumn& target() const { return ROMSPointingColumns::target();} const ROArrayMeasColumn& targetMeasCol()const { return ROMSPointingColumns::targetMeasCol();} const ROScalarColumn& tracking() const { return ROMSPointingColumns::tracking();} // // Access to optional columns // const ROArrayColumn& pointingOffset() const { return ROMSPointingColumns::pointingOffset();} const ROArrayMeasColumn& pointingOffsetMeasCol() const { return ROMSPointingColumns::pointingOffsetMeasCol();} const ROArrayColumn& sourceOffset() const { return ROMSPointingColumns::sourceOffset();} const ROArrayMeasColumn& sourceOffsetMeasCol() const { return ROMSPointingColumns::sourceOffsetMeasCol();} const ROArrayColumn& encoder() const { return ROMSPointingColumns::encoder();} const ROScalarMeasColumn& encoderMeas() const { return ROMSPointingColumns::encoderMeas();} const ROScalarColumn& pointingModelId() const { return ROMSPointingColumns::pointingModelId();} const ROScalarColumn& onSource() const { return ROMSPointingColumns::onSource();} const ROScalarColumn& overTheTop() const { return ROMSPointingColumns::overTheTop();} // // 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(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(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; }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/ms/MeasurementSets/MSPointingEnums.h000066400000000000000000000101241321422335000222420ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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-2.4.1/ms/MeasurementSets/MSPolColumns.cc000066400000000000000000000124201321422335000216750ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN ROMSPolarizationColumns:: ROMSPolarizationColumns(const MSPolarization& msPolarization): corrProduct_p(msPolarization, MSPolarization:: columnName(MSPolarization::CORR_PRODUCT)), corrType_p(msPolarization, MSPolarization:: columnName(MSPolarization::CORR_TYPE)), flagRow_p(msPolarization, MSPolarization:: columnName(MSPolarization::FLAG_ROW)), numCorr_p(msPolarization, MSPolarization:: columnName(MSPolarization::NUM_CORR)) {} ROMSPolarizationColumns::~ROMSPolarizationColumns() {} ROMSPolarizationColumns::ROMSPolarizationColumns(): corrProduct_p(), corrType_p(), flagRow_p(), numCorr_p() { } void ROMSPolarizationColumns:: 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)); } Int ROMSPolarizationColumns:: match(const Vector& polType, Int tryRow) { uInt 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 uInt tr = tryRow; if (tr >= r) { throw(AipsError("ROMSPolarszationColumns::match(...) - " "the row 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 ROMSPolarizationColumns:: matchCorrType(uInt row, const Vector& polType) const { DebugAssert(row < nrow(), AipsError); return allEQ(corrType()(row), polType); } Bool ROMSPolarizationColumns:: matchCorrProduct(uInt 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)); } MSPolarizationColumns:: MSPolarizationColumns(MSPolarization& msPolarization): ROMSPolarizationColumns(msPolarization), corrProduct_p(msPolarization, MSPolarization:: columnName(MSPolarization::CORR_PRODUCT)), corrType_p(msPolarization, MSPolarization:: columnName(MSPolarization::CORR_TYPE)), flagRow_p(msPolarization, MSPolarization:: columnName(MSPolarization::FLAG_ROW)), numCorr_p(msPolarization, MSPolarization:: columnName(MSPolarization::NUM_CORR)) {} MSPolarizationColumns::~MSPolarizationColumns() {} MSPolarizationColumns::MSPolarizationColumns(): ROMSPolarizationColumns(), corrProduct_p(), corrType_p(), flagRow_p(), numCorr_p() { } void MSPolarizationColumns:: attach(MSPolarization& msPolarization) { ROMSPolarizationColumns::attach(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)); } // Local Variables: // compile-command: "gmake MSPolColumns" // End: } //# NAMESPACE CASACORE - END casacore-2.4.1/ms/MeasurementSets/MSPolColumns.h000066400000000000000000000167221321422335000215500ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef MS_MSPOLCOLUMNS_H #define MS_MSPOLCOLUMNS_H #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN class MSPolarization; template class Vector; template class Matrix; // // A class to provide easy read-only access to MSPolarization columns // // // // // //
      • MSPolarization //
      • ArrayColumn //
      • ScalarColumn // // // // ROMSPolarizationColumns stands for Read-Only MeasurementSet Polarization Table columns. // // // // This class provides read-only 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 ROMSColumns for an example. // // // // See MSColumns for the motivation. // class ROMSPolarizationColumns { public: // Create a columns object that accesses the data in the specified Table ROMSPolarizationColumns(const MSPolarization& msPolarization); // The destructor does nothing special ~ROMSPolarizationColumns(); // Access to required columns // const ROArrayColumn& corrProduct() const {return corrProduct_p;} const ROArrayColumn& corrType() const {return corrType_p;} const ROScalarColumn& flagRow() const {return flagRow_p;} const ROScalarColumn& numCorr() const {return numCorr_p;} // // Convenience function that returns the number of rows in any of the columns uInt 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. Int match(const Vector& polType, Int tryRow=-1); protected: //# default constructor creates a object that is not usable. Use the attach //# function correct this. ROMSPolarizationColumns(); //# attach this object to the supplied table. void attach(const MSPolarization& msPolarization); private: //# Functions which check the supplied values against the relevant column and //# the specified row. Bool matchCorrType(uInt row, const Vector& polType) const; Bool matchCorrProduct(uInt row, const Matrix& polProduct) const; //# Make the assignment operator and the copy constructor private to prevent //# any compiler generated one from being used. ROMSPolarizationColumns(const ROMSPolarizationColumns&); ROMSPolarizationColumns& operator=(const ROMSPolarizationColumns&); //# required columns ROArrayColumn corrProduct_p; ROArrayColumn corrType_p; ROScalarColumn flagRow_p; ROScalarColumn numCorr_p; }; // // A class to provide easy read-write 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 ROMSPolarizationColumns { public: // Create a columns object that accesses the data in the specified Table MSPolarizationColumns(MSPolarization& msPolarization); // The destructor does nothing special ~MSPolarizationColumns(); // Read-write access to required columns // ArrayColumn& corrProduct() {return corrProduct_p;} ArrayColumn& corrType() {return corrType_p;} ScalarColumn& flagRow() {return flagRow_p;} ScalarColumn& numCorr() {return numCorr_p;} // // Read-only access to required columns // const ROArrayColumn& corrProduct() const { return ROMSPolarizationColumns::corrProduct();} const ROArrayColumn& corrType() const { return ROMSPolarizationColumns::corrType();} const ROScalarColumn& flagRow() const { return ROMSPolarizationColumns::flagRow();} const ROScalarColumn& numCorr() const { return ROMSPolarizationColumns::numCorr();} // 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(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&); //# required columns ArrayColumn corrProduct_p; ArrayColumn corrType_p; ScalarColumn flagRow_p; ScalarColumn numCorr_p; }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/ms/MeasurementSets/MSPolEnums.h000066400000000000000000000066041321422335000212150ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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-2.4.1/ms/MeasurementSets/MSPolarization.cc000066400000000000000000000136541321422335000222670ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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, uInt 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 &, uInt, 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; } void MSPolarization::init() { if (! columnMap_p.ndefined()) { // the PredefinedColumns // CORR_PRODUCT colMapDef(CORR_PRODUCT, "CORR_PRODUCT", TpArrayInt, "Indices describing receptors of feed going into correlation","",""); // CORR_TYPE colMapDef(CORR_TYPE, "CORR_TYPE", TpArrayInt, "The polarization type for each correlation product," " as a Stokes enum.","",""); // FLAG_ROW colMapDef(FLAG_ROW, "FLAG_ROW", TpBool, "Row flag","",""); // NUM_CORR colMapDef(NUM_CORR, "NUM_CORR", TpInt, "Number of correlation products","",""); // PredefinedKeywords // init requiredTableDesc TableDesc requiredTD; // all required keywords uInt i; for (i = UNDEFINED_KEYWORD+1; i <= NUMBER_PREDEFINED_KEYWORDS; i++) { addKeyToDesc(requiredTD, PredefinedKeywords(i)); } // all required columns // First define the columns with known dimensionality addColumnToDesc(requiredTD, CORR_TYPE, 1); addColumnToDesc(requiredTD, CORR_PRODUCT, 2); // Now define all other columns (duplicates are skipped) for (i = UNDEFINED_COLUMN+1; i <= NUMBER_REQUIRED_COLUMNS; i++) { addColumnToDesc(requiredTD, PredefinedColumns(i)); } requiredTD_p=new TableDesc(requiredTD); } } MSPolarization MSPolarization::referenceCopy(const String& newTableName, const Block& writableColumns) const { return MSPolarization(MSTable:: referenceCopy(newTableName,writableColumns)); } } //# NAMESPACE CASACORE - END casacore-2.4.1/ms/MeasurementSets/MSPolarization.h000066400000000000000000000117401321422335000221230ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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, uInt 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 void init(); private: // required by the need to throw an exception in the destructor Bool hasBeenDestroyed_p; }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/ms/MeasurementSets/MSProcessor.cc000066400000000000000000000132601321422335000215640ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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, uInt 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 &, uInt, 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; } void MSProcessor::init() { if (! columnMap_p.ndefined()) { // the PredefinedColumns // FLAG_ROW colMapDef(FLAG_ROW, "FLAG_ROW", TpBool, "Row flag","",""); // colMapDef(MODE_ID, "MODE_ID", TpInt, "Processor mode id","",""); // PASS_ID colMapDef(PASS_ID, "PASS_ID", TpInt, "Processor pass number","",""); // TYPE colMapDef(TYPE, "TYPE", TpString, "Processor type","",""); // TYPE_ID colMapDef(TYPE_ID, "TYPE_ID", TpInt, "Processor type id","",""); // SUB_TYPE colMapDef(SUB_TYPE, "SUB_TYPE", TpString, "Processor sub type","",""); // PredefinedKeywords // init requiredTableDesc TableDesc requiredTD; // all required keywords uInt i; for (i = UNDEFINED_KEYWORD+1; i <= NUMBER_PREDEFINED_KEYWORDS; i++) { addKeyToDesc(requiredTD, PredefinedKeywords(i)); } // all required columns // Now define all other columns (duplicates are skipped) for (i = UNDEFINED_COLUMN+1; i <= NUMBER_REQUIRED_COLUMNS; i++) { addColumnToDesc(requiredTD, PredefinedColumns(i)); } requiredTD_p=new TableDesc(requiredTD); } } MSProcessor MSProcessor::referenceCopy(const String& newTableName, const Block& writableColumns) const { return MSProcessor(MSTable:: referenceCopy(newTableName,writableColumns)); } } //# NAMESPACE CASACORE - END casacore-2.4.1/ms/MeasurementSets/MSProcessor.h000066400000000000000000000115341321422335000214300ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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, uInt 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 void init(); private: // required by the need to throw an exception in the destructor Bool hasBeenDestroyed_p; }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/ms/MeasurementSets/MSProcessorColumns.cc000066400000000000000000000114131321422335000231230ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN ROMSProcessorColumns:: ROMSProcessorColumns(const MSProcessor& msProcessor): flagRow_p(msProcessor, MSProcessor::columnName(MSProcessor::FLAG_ROW)), modeId_p(msProcessor, MSProcessor::columnName(MSProcessor::MODE_ID)), type_p(msProcessor, MSProcessor::columnName(MSProcessor::TYPE)), typeId_p(msProcessor, MSProcessor::columnName(MSProcessor::TYPE_ID)), subType_p(msProcessor, MSProcessor::columnName(MSProcessor::SUB_TYPE)), passId_p() { attachOptionalCols(msProcessor); } ROMSProcessorColumns::~ROMSProcessorColumns() {} ROMSProcessorColumns::ROMSProcessorColumns(): flagRow_p(), modeId_p(), type_p(), typeId_p(), subType_p(), passId_p() { } void ROMSProcessorColumns::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 ROMSProcessorColumns:: 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); } MSProcessorColumns::MSProcessorColumns(MSProcessor& msProcessor): ROMSProcessorColumns(msProcessor), flagRow_p(msProcessor, MSProcessor::columnName(MSProcessor::FLAG_ROW)), modeId_p(msProcessor, MSProcessor::columnName(MSProcessor::MODE_ID)), type_p(msProcessor, MSProcessor::columnName(MSProcessor::TYPE)), typeId_p(msProcessor, MSProcessor::columnName(MSProcessor::TYPE_ID)), subType_p(msProcessor, MSProcessor::columnName(MSProcessor::SUB_TYPE)) { const ColumnDescSet& cds=msProcessor.tableDesc().columnDescSet(); const String& passId=MSProcessor::columnName(MSProcessor::PASS_ID); if (cds.isDefined(passId)) passId_p.attach(msProcessor, passId); } MSProcessorColumns::~MSProcessorColumns() {} MSProcessorColumns::MSProcessorColumns(): ROMSProcessorColumns(), flagRow_p(), modeId_p(), type_p(), typeId_p(), subType_p(), passId_p() { } void MSProcessorColumns::attach(MSProcessor& msProcessor) { ROMSProcessorColumns::attach(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(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); } // Local Variables: // compile-command: "gmake MSProcessorColumns" // End: } //# NAMESPACE CASACORE - END casacore-2.4.1/ms/MeasurementSets/MSProcessorColumns.h000066400000000000000000000166541321422335000230010ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef MS_MSPROCESSORCOLUMNS_H #define MS_MSPROCESSORCOLUMNS_H #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN class MSProcessor; // // A class to provide easy read-only access to MSProcessor columns // // // // // //
      • MSProcessor //
      • ArrayColumn //
      • ScalarColumn // // // // ROMSProcessorColumns stands for Read-Only MeasurementSet Processor Table columns. // // // // This class provides read-only 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 // ROMSColumns for an example. // // // // See MSColumns for the motivation. // class ROMSProcessorColumns { public: // Create a columns object that accesses the data in the specified Table ROMSProcessorColumns(const MSProcessor& msProcessor); // The destructor does nothing special ~ROMSProcessorColumns(); // Access to required columns // const ROScalarColumn& flagRow() const {return flagRow_p;} const ROScalarColumn& modeId() const {return modeId_p;} const ROScalarColumn& type() const {return type_p;} const ROScalarColumn& typeId() const {return typeId_p;} const ROScalarColumn& subType() const {return subType_p;} // // Read-only access to optional columns // const ROScalarColumn& passId() const {return passId_p;} // // Convenience function that returns the number of rows in any of the columns uInt nrow() const {return flagRow_p.nrow();} protected: //# default constructor creates a object that is not usable. Use the attach //# function correct this. ROMSProcessorColumns(); //# 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. ROMSProcessorColumns(const ROMSProcessorColumns&); ROMSProcessorColumns& operator=(const ROMSProcessorColumns&); //# Check if any optional columns exist and if so attach them. void attachOptionalCols(const MSProcessor& msField); //# required columns ROScalarColumn flagRow_p; ROScalarColumn modeId_p; ROScalarColumn type_p; ROScalarColumn typeId_p; ROScalarColumn subType_p; //# optional columns ROScalarColumn passId_p; }; // // A class to provide easy read-write 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 ROMSProcessorColumns { public: // Create a columns object that accesses the data in the specified Table MSProcessorColumns(MSProcessor& msProcessor); // The destructor does nothing special ~MSProcessorColumns(); // Read-write 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;} // // Read-write access to optional columns // ScalarColumn& passId() {return passId_p;} // // Read-only access to required columns // const ROScalarColumn& flagRow() const { return ROMSProcessorColumns::flagRow();} const ROScalarColumn& modeId() const { return ROMSProcessorColumns::modeId();} const ROScalarColumn& type() const { return ROMSProcessorColumns::type();} const ROScalarColumn& typeId() const { return ROMSProcessorColumns::typeId();} const ROScalarColumn& subType() const { return ROMSProcessorColumns::subType();} // // Read-only access to optional columns // const ROScalarColumn& passId() const { return ROMSProcessorColumns::passId();} // 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(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(MSProcessor& msProcessor); //# required columns ScalarColumn flagRow_p; ScalarColumn modeId_p; ScalarColumn type_p; ScalarColumn typeId_p; ScalarColumn subType_p; //# optional columns ScalarColumn passId_p; }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/ms/MeasurementSets/MSProcessorEnums.h000066400000000000000000000063451321422335000224440ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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-2.4.1/ms/MeasurementSets/MSRange.cc000066400000000000000000000445561321422335000206550ustar00rootroot00000000000000///# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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) { ROScalarColumn dd(ms_p,MS::columnName(MS::DATA_DESC_ID)); Vector ddId=scalarRange(dd); ddId_p=ddId; } Int n2=ddId_p.nelements(); ROMSDataDescColumns 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 ROMSSpWindowColumns spwc(ms_p.spectralWindow()); ROMSPolarizationColumns 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(polId_p); if (fld==MSS::CORR_NAMES) { Matrix names(corrTypes.shape()); for (uInt 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(polId_p)); } break; case MSS::NUM_CHAN: { checkShapes(); out.define(keyword,msc.spectralWindow().numChan().getColumnCells(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 Int nField = ms_p.field().nrow(); Matrix phaseDir(2,nField); Vector dir(2); for (Int i=0; i rowNumbers=ms_p.rowNumbers(); Vector rows(n); convertArray(rows,rowNumbers); if (oneBased) rows+=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(); Int 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 ROScalarColumn& id, Bool oneBased) { Vector ids=scalarRange(id); if (oneBased) ids+=1; out.define(item,ids); } Vector MSRange::scalarRange(const ROScalarColumn& id) { const Int option=Sort::HeapSort | Sort::NoDuplicates; const Sort::Order order=Sort::Ascending; Vector idvec=id.getColumn(); Int n=GenSort::sort (idvec, order, option); Vector ids=idvec(Slice(0,n)); return ids; } void MSRange::minMax(Float& mini, Float& maxi, const ROArrayColumn& data, const ROArrayColumn& flag, Bool useFlags) { IPosition shp=data.shape(0); Int nrow=data.nrow(); Int numrow=Int(blockSize_p*1.0e6/(sizeof(Float)*shp(0)*shp(1))); for (Int 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 ROArrayColumn& data1, const ROArrayColumn& data2, const ROArrayColumn& flag, Int dataType, Bool useFlags) { IPosition shp=data1.shape(0); Int nrow=data1.nrow(); Int numrow=Int(blockSize_p*1.0e6/(sizeof(Complex)*shp(0)*shp(1))); for (Int 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 ROScalarColumn& ant1, const ROScalarColumn& 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; Int n=GenSort::sort (a1, order, option); return a1(Slice(0,n)); } } //# NAMESPACE CASACORE - END casacore-2.4.1/ms/MeasurementSets/MSRange.h000066400000000000000000000162311321422335000205040ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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-2.4.1/ms/MeasurementSets/MSSource.cc000066400000000000000000000162101321422335000210430ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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, uInt 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 &, uInt, 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; } void MSSource::init() { if (! columnMap_p.ndefined()) { // the PredefinedColumns // CALIBRATION_GROUP colMapDef(CALIBRATION_GROUP, "CALIBRATION_GROUP", TpInt, "Number of grouping for calibration purpose.","",""); // CODE colMapDef(CODE, "CODE", TpString, "Special characteristics of source, " "e.g. Bandpass calibrator","",""); // DIRECTION colMapDef(DIRECTION, "DIRECTION", TpArrayDouble, "Direction (e.g. RA, DEC).","rad","Direction"); // INTERVAL colMapDef(INTERVAL, "INTERVAL", TpDouble, "Interval of time for which this set of parameters " "is accurate","s",""); // NAME colMapDef(NAME, "NAME", TpString, "Name of source as given during observations","",""); // NUM_LINES colMapDef(NUM_LINES, "NUM_LINES", TpInt, "Number of spectral lines","",""); // POSITION colMapDef(POSITION, "POSITION", TpArrayDouble, "Position (e.g. for solar system objects", "m","Position"); // PROPER_MOTION colMapDef(PROPER_MOTION, "PROPER_MOTION", TpArrayDouble, "Proper motion","rad/s",""); // PULSAR_ID colMapDef(PULSAR_ID, "PULSAR_ID", TpInt, "Pulsar Id, pointer to pulsar table","",""); // REST_FREQUENCY colMapDef(REST_FREQUENCY, "REST_FREQUENCY", TpArrayDouble, "Line rest frequency","Hz","Frequency"); // SOURCE_ID colMapDef(SOURCE_ID, "SOURCE_ID", TpInt, "Source id","",""); // SOURCE_MODEL colMapDef(SOURCE_MODEL, "SOURCE_MODEL", TpRecord, "Component Source Model","",""); // SPECTRAL_WINDOW_ID colMapDef(SPECTRAL_WINDOW_ID,"SPECTRAL_WINDOW_ID",TpInt, "ID for this spectral window setup","",""); // SYSVEL colMapDef(SYSVEL, "SYSVEL", TpArrayDouble, "Systemic velocity at reference","m/s","Radialvelocity"); // TIME colMapDef(TIME, "TIME", TpDouble, "Midpoint of time for which this set of parameters " "is accurate.","s","Epoch"); // TRANSITION colMapDef(TRANSITION, "TRANSITION", TpArrayString, "Line Transition name","",""); // PredefinedKeywords // init requiredTableDesc TableDesc requiredTD; // all required keywords uInt i; for (i = UNDEFINED_KEYWORD+1; i <= NUMBER_PREDEFINED_KEYWORDS; i++) { addKeyToDesc(requiredTD, PredefinedKeywords(i)); } // all required columns // First define the columns with fixed size arrays IPosition shape(1,2); ColumnDesc::Option option=ColumnDesc::Direct; addColumnToDesc(requiredTD, DIRECTION, shape, option); addColumnToDesc(requiredTD, PROPER_MOTION, shape, option); // Now define all other columns (duplicates are skipped) for (i = UNDEFINED_COLUMN+1; i <= NUMBER_REQUIRED_COLUMNS; i++) { addColumnToDesc(requiredTD, PredefinedColumns(i)); } requiredTD_p=new TableDesc(requiredTD); } } MSSource MSSource::referenceCopy(const String& newTableName, const Block& writableColumns) const { return MSSource(MSTable:: referenceCopy(newTableName,writableColumns)); } } //# NAMESPACE CASACORE - END casacore-2.4.1/ms/MeasurementSets/MSSource.h000066400000000000000000000114261321422335000207110ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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, uInt 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 void init(); private: // required by the need to throw an exception in the destructor Bool hasBeenDestroyed_p; }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/ms/MeasurementSets/MSSourceColumns.cc000066400000000000000000000246161321422335000224150ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN ROMSSourceColumns::ROMSSourceColumns(const MSSource& msSource): isNull_p(True), calibrationGroup_p(), code_p(), direction_p(), interval_p(), name_p(), numLines_p(), properMotion_p(), spectralWindowId_p(), time_p(), position_p(), pulsarId_p(), restFrequency_p(), sourceModel_p(), sysvel_p(), transition_p(), directionMeas_p(), timeMeas_p(), positionMeas_p(), restFrequencyMeas_p(), sysvelMeas_p(), directionQuant_p(), intervalQuant_p(), properMotionQuant_p(), timeQuant_p(), positionQuant_p(), restFrequencyQuant_p(), sysvelQuant_p() { attach(msSource); } ROMSSourceColumns::~ROMSSourceColumns() {} ROMSSourceColumns::ROMSSourceColumns(): isNull_p(True), calibrationGroup_p(), code_p(), direction_p(), interval_p(), name_p(), numLines_p(), properMotion_p(), sourceId_p(), spectralWindowId_p(), time_p(), position_p(), pulsarId_p(), restFrequency_p(), sourceModel_p(), sysvel_p(), transition_p(), directionMeas_p(), timeMeas_p(), positionMeas_p(), restFrequencyMeas_p(), sysvelMeas_p(), directionQuant_p(), intervalQuant_p(), properMotionQuant_p(), timeQuant_p(), positionQuant_p(), restFrequencyQuant_p(), sysvelQuant_p() { } void ROMSSourceColumns::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 ROMSSourceColumns::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); } MSSourceColumns::MSSourceColumns(MSSource& msSource): ROMSSourceColumns(), calibrationGroup_p(), code_p(), direction_p(), interval_p(), name_p(), numLines_p(), properMotion_p(), spectralWindowId_p(), time_p(), position_p(), pulsarId_p(), restFrequency_p(), sourceModel_p(), sysvel_p(), transition_p(), directionMeas_p(), timeMeas_p(), positionMeas_p(), restFrequencyMeas_p(), sysvelMeas_p(), directionQuant_p(), intervalQuant_p(), properMotionQuant_p(), timeQuant_p(), positionQuant_p(), restFrequencyQuant_p(), sysvelQuant_p() { attach(msSource); } MSSourceColumns::~MSSourceColumns() {} MSSourceColumns::MSSourceColumns(): ROMSSourceColumns(), calibrationGroup_p(), code_p(), direction_p(), interval_p(), name_p(), numLines_p(), properMotion_p(), sourceId_p(), spectralWindowId_p(), time_p(), position_p(), pulsarId_p(), restFrequency_p(), sourceModel_p(), sysvel_p(), transition_p(), directionMeas_p(), timeMeas_p(), positionMeas_p(), restFrequencyMeas_p(), sysvelMeas_p(), directionQuant_p(), intervalQuant_p(), properMotionQuant_p(), timeQuant_p(), positionQuant_p(), restFrequencyQuant_p(), sysvelQuant_p() { } void MSSourceColumns::attach(MSSource& msSource) { ROMSSourceColumns::attach(msSource); 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(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); } } // Local Variables: // compile-command: "gmake MSSourceColumns" // End: } //# NAMESPACE CASACORE - END casacore-2.4.1/ms/MeasurementSets/MSSourceColumns.h000066400000000000000000000425261321422335000222570ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 read-only access to MSSource columns // // // // // //
      • MSSource //
      • ArrayColumn //
      • ScalarColumn // // // // ROMSSourceColumns stands for Read-Only MeasurementSet Source Table columns. // // // // This class provides read-only 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 ROMSColumns for an example. // // // // See MSColumns for the motivation. // class ROMSSourceColumns { public: // Construct from the supplied Table ROMSSourceColumns(const MSSource& msSource); // The destructor does nothing special ~ROMSSourceColumns(); // Is this object defined? (MSSource table is optional) Bool isNull() const {return isNull_p;} // Access to required columns // const ROScalarColumn& calibrationGroup() const { return calibrationGroup_p;} const ROScalarColumn& code() const {return code_p;} const ROArrayColumn& direction() const {return direction_p;} const ROArrayQuantColumn& directionQuant() const { return directionQuant_p;} const ROScalarMeasColumn& directionMeas() const { return directionMeas_p;} const ROScalarColumn& interval() const {return interval_p;} const ROScalarQuantColumn& intervalQuant() const { return intervalQuant_p;} const ROScalarColumn& name() const {return name_p;} const ROScalarColumn& numLines() const {return numLines_p;} const ROArrayColumn& properMotion() const {return properMotion_p;} const ROArrayQuantColumn& properMotionQuant() const { return properMotionQuant_p;} const ROScalarColumn& sourceId() const {return sourceId_p;} const ROScalarColumn& spectralWindowId() const { return spectralWindowId_p;} const ROScalarColumn& time() const {return time_p;} const ROScalarQuantColumn& timeQuant() const {return timeQuant_p;} const ROScalarMeasColumn& timeMeas() const {return timeMeas_p;} // // Access to optional columns // const ROArrayColumn& position() const {return position_p;} const ROArrayQuantColumn& positionQuant() const { return positionQuant_p;} const ROScalarMeasColumn& positionMeas() const { return positionMeas_p;} const ROScalarColumn& pulsarId() const {return pulsarId_p;} const ROArrayColumn& restFrequency() const {return restFrequency_p;} const ROArrayQuantColumn& restFrequencyQuant() const { return restFrequencyQuant_p;} const ROArrayMeasColumn& restFrequencyMeas() const { return restFrequencyMeas_p;} const ROScalarColumn& sourceModel() const { return sourceModel_p;} const ROArrayColumn& sysvel() const {return sysvel_p;} const ROArrayQuantColumn& sysvelQuant() const {return sysvelQuant_p;} const ROArrayMeasColumn& sysvelMeas() const { return sysvelMeas_p;} const ROArrayColumn& 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. uInt nrow() const {return isNull() ? 0 : calibrationGroup_p.nrow();} protected: //# default constructor creates a object that is not usable. Use the attach //# function correct this. ROMSSourceColumns(); //# 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. ROMSSourceColumns(const ROMSSourceColumns&); ROMSSourceColumns& operator=(const ROMSSourceColumns&); //# 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 ROScalarColumn calibrationGroup_p; ROScalarColumn code_p; ROArrayColumn direction_p; ROScalarColumn interval_p; ROScalarColumn name_p; ROScalarColumn numLines_p; ROArrayColumn properMotion_p; ROScalarColumn sourceId_p; ROScalarColumn spectralWindowId_p; ROScalarColumn time_p; //# optional columns ROArrayColumn position_p; ROScalarColumn pulsarId_p; ROArrayColumn restFrequency_p; ROScalarColumn sourceModel_p; ROArrayColumn sysvel_p; ROArrayColumn transition_p; //# Access to Measure columns ROScalarMeasColumn directionMeas_p; ROScalarMeasColumn timeMeas_p; //# Optional Measure columns ROScalarMeasColumn positionMeas_p; ROArrayMeasColumn restFrequencyMeas_p; ROArrayMeasColumn sysvelMeas_p; //# Access to Quantum columns ROArrayQuantColumn directionQuant_p; ROScalarQuantColumn intervalQuant_p; ROArrayQuantColumn properMotionQuant_p; ROScalarQuantColumn timeQuant_p; //# Optional Quantum columns ROArrayQuantColumn positionQuant_p; ROArrayQuantColumn restFrequencyQuant_p; ROArrayQuantColumn sysvelQuant_p; }; // // A class to provide easy read-write 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 ROMSSourceColumns { public: // Construct from the supplied Table MSSourceColumns(MSSource& msSource); // The destructor does nothing special ~MSSourceColumns(); // Read-write 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;} // // Read-write 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;} // // Read-only access to required columns // const ROScalarColumn& calibrationGroup() const { return ROMSSourceColumns::calibrationGroup();} const ROScalarColumn& code() const { return ROMSSourceColumns::code();} const ROArrayColumn& direction() const { return ROMSSourceColumns::direction();} const ROArrayQuantColumn& directionQuant() const { return ROMSSourceColumns::directionQuant();} const ROScalarMeasColumn& directionMeas() const { return ROMSSourceColumns::directionMeas();} const ROScalarColumn& interval() const { return ROMSSourceColumns::interval();} const ROScalarQuantColumn& intervalQuant() const { return ROMSSourceColumns::intervalQuant();} const ROScalarColumn& name() const { return ROMSSourceColumns::name();} const ROScalarColumn& numLines() const { return ROMSSourceColumns::numLines();} const ROArrayColumn& properMotion() const { return ROMSSourceColumns::properMotion();} const ROArrayQuantColumn& properMotionQuant() const { return ROMSSourceColumns::properMotionQuant();} const ROScalarColumn& sourceId() const { return ROMSSourceColumns::sourceId();} const ROScalarColumn& spectralWindowId() const { return ROMSSourceColumns::spectralWindowId();} const ROScalarColumn& time() const { return ROMSSourceColumns::time();} const ROScalarQuantColumn& timeQuant() const { return ROMSSourceColumns::timeQuant();} const ROScalarMeasColumn& timeMeas() const { return ROMSSourceColumns::timeMeas();} // // Read-only access to optional columns // const ROArrayColumn& position() const { return ROMSSourceColumns::position();} const ROArrayQuantColumn& positionQuant() const { return ROMSSourceColumns::positionQuant();} const ROScalarMeasColumn& positionMeas() const { return ROMSSourceColumns::positionMeas();} const ROScalarColumn& pulsarId() const { return ROMSSourceColumns::pulsarId();} const ROArrayColumn& restFrequency() const { return ROMSSourceColumns::restFrequency();} const ROArrayQuantColumn& restFrequencyQuant() const { return ROMSSourceColumns::restFrequencyQuant();} const ROArrayMeasColumn& restFrequencyMeas() const { return ROMSSourceColumns::restFrequencyMeas();} const ROScalarColumn& sourceModel() const { return ROMSSourceColumns::sourceModel();} const ROArrayColumn& sysvel() const { return ROMSSourceColumns::sysvel();} const ROArrayQuantColumn& sysvelQuant() const { return ROMSSourceColumns::sysvelQuant();} const ROArrayMeasColumn& sysvelMeas() const { return ROMSSourceColumns::sysvelMeas();} const ROArrayColumn& transition() const { return ROMSSourceColumns::transition();} // // 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(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(MSSource& msSource); //# 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; }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/ms/MeasurementSets/MSSourceEnums.h000066400000000000000000000104171321422335000217200ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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-2.4.1/ms/MeasurementSets/MSSpWindowColumns.cc000066400000000000000000000713251321422335000227260ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include #include #include #include #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 ROMSSpWindowColumns:: ROMSSpWindowColumns(const MSSpectralWindow& msSpWindow): chanFreq_p(msSpWindow, MSSpectralWindow:: columnName(MSSpectralWindow::CHAN_FREQ)), chanWidth_p(msSpWindow, MSSpectralWindow:: columnName(MSSpectralWindow::CHAN_WIDTH)), effectiveBW_p(msSpWindow, MSSpectralWindow:: columnName(MSSpectralWindow::EFFECTIVE_BW)), flagRow_p(msSpWindow, MSSpectralWindow:: columnName(MSSpectralWindow::FLAG_ROW)), freqGroup_p(msSpWindow, MSSpectralWindow:: columnName(MSSpectralWindow::FREQ_GROUP)), freqGroupName_p(msSpWindow, MSSpectralWindow:: columnName(MSSpectralWindow::FREQ_GROUP_NAME)), ifConvChain_p(msSpWindow, MSSpectralWindow:: columnName(MSSpectralWindow::IF_CONV_CHAIN)), measFreqRef_p(msSpWindow, MSSpectralWindow:: columnName(MSSpectralWindow::MEAS_FREQ_REF)), name_p(msSpWindow, MSSpectralWindow:: columnName(MSSpectralWindow::NAME)), netSideband_p(msSpWindow, MSSpectralWindow:: columnName(MSSpectralWindow::NET_SIDEBAND)), numChan_p(msSpWindow, MSSpectralWindow:: columnName(MSSpectralWindow::NUM_CHAN)), refFrequency_p(msSpWindow, MSSpectralWindow:: columnName(MSSpectralWindow::REF_FREQUENCY)), resolution_p(msSpWindow, MSSpectralWindow:: columnName(MSSpectralWindow::RESOLUTION)), totalBandwidth_p(msSpWindow, MSSpectralWindow:: columnName(MSSpectralWindow::TOTAL_BANDWIDTH)), assocNature_p(), assocSpwId_p(), bbcNo_p(), bbcSideband_p(), dopplerId_p(), receiverId_p(), chanFreqMeas_p(msSpWindow, MSSpectralWindow:: columnName(MSSpectralWindow::CHAN_FREQ)), refFrequencyMeas_p(msSpWindow, MSSpectralWindow:: columnName(MSSpectralWindow::REF_FREQUENCY)), chanFreqQuant_p(msSpWindow, MSSpectralWindow:: columnName(MSSpectralWindow::CHAN_FREQ)), chanWidthQuant_p(msSpWindow, MSSpectralWindow:: columnName(MSSpectralWindow::CHAN_WIDTH)), effectiveBWQuant_p(msSpWindow, MSSpectralWindow:: columnName(MSSpectralWindow::EFFECTIVE_BW)), refFrequencyQuant_p(msSpWindow, MSSpectralWindow:: columnName(MSSpectralWindow::REF_FREQUENCY)), resolutionQuant_p(msSpWindow, MSSpectralWindow:: columnName(MSSpectralWindow::RESOLUTION)), totalBandwidthQuant_p(msSpWindow, MSSpectralWindow:: columnName(MSSpectralWindow::TOTAL_BANDWIDTH)) { attachOptionalCols(msSpWindow); } ROMSSpWindowColumns::~ROMSSpWindowColumns() {} Int ROMSSpWindowColumns:: matchSpw(const MFrequency& refFreq, uInt nChan, const Quantum& bandwidth, Int ifChain, const Quantum& tolerance, Int tryRow) const { uInt 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 uInt tr = tryRow; if (tr >= r) { throw(AipsError("ROMSSpWindowColumns::match(...) - " "the row 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. Int ROMSSpWindowColumns:: 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, Int tryRow) const { uInt 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 uInt tr = tryRow; if (tr >= r) { throw(AipsError("ROMSSpWindowColumns::match(...) - " "the row 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; } Vector ROMSSpWindowColumns:: allMatchedSpw(const MFrequency& refFreq, uInt nChan, const Quantum& bandwidth, Int ifChain, const Quantum& tolerance) const { uInt r = nrow(); Vector 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); Int numMatch=0; for (uInt 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; } Int ROMSSpWindowColumns:: matchSpw(const MFrequency& refFreq, uInt nChan, const Quantum& bandwidth, Int ifChain, const Quantum& tolerance, Vector& otherFreqs, Bool& reversed) const { reversed=False; Int matchedSpw=-1; Vector allMatchSpw= allMatchedSpw(refFreq, nChan, bandwidth, ifChain, tolerance); Int nMatches=allMatchSpw.shape()(0); 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 (Int k=0; k < nMatches; ++k){ matchedSpw=allMatchSpw[k]; if(matchChanFreq(matchedSpw, otherFreqs, tolInHz)){ return matchedSpw; } else{ Vector reverseFreq(otherFreqs.shape()); for (uInt k=0; k < nChan ; ++k){ reverseFreq[k]=otherFreqs[nChan-1-k]; } if(matchChanFreq(matchedSpw, reverseFreq, tolInHz)){ reversed=True; return matchedSpw; } } } return -1; } ROMSSpWindowColumns::ROMSSpWindowColumns(): chanFreq_p(), chanWidth_p(), effectiveBW_p(), flagRow_p(), freqGroup_p(), freqGroupName_p(), ifConvChain_p(), measFreqRef_p(), name_p(), netSideband_p(), numChan_p(), refFrequency_p(), resolution_p(), totalBandwidth_p(), assocNature_p(), assocSpwId_p(), bbcNo_p(), bbcSideband_p(), dopplerId_p(), receiverId_p(), chanFreqMeas_p(), refFrequencyMeas_p(), chanFreqQuant_p(), chanWidthQuant_p(), effectiveBWQuant_p(), refFrequencyQuant_p(), resolutionQuant_p(), totalBandwidthQuant_p() { } void ROMSSpWindowColumns::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 ROMSSpWindowColumns:: 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); } Bool ROMSSpWindowColumns:: matchRefFrequency(uInt 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 ROMSSpWindowColumns:: matchRefFreqCnvtrd(uInt 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<< "[ROMSSpWindowColumns::matchRefFreqCnvtr()] match reference frequency. "<< endl; }else{ rowFreq = Vector(chanFreqMeas()(row))(0); //cout<< "[ROMSSpWindowColumns::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 ROMSSpWindowColumns:: matchChanFreq(uInt 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 ROMSSpWindowColumns:: matchIfConvChain(uInt row, Int ifChain) const { DebugAssert(row < nrow(), AipsError); return ifChain == ifConvChain()(row); } Bool ROMSSpWindowColumns:: matchTotalBandwidth(uInt row, Double bandwidthInHz, Double tolInHz) const { DebugAssert(row < nrow(), AipsError); return nearAbs(totalBandwidth()(row), bandwidthInHz, fabs(tolInHz)); } Bool ROMSSpWindowColumns:: matchNumChan(uInt row, Int nChan) const { DebugAssert(row < nrow(), AipsError); return nChan == numChan()(row); } MSSpWindowColumns::MSSpWindowColumns(MSSpectralWindow& msSpWindow): ROMSSpWindowColumns(msSpWindow), chanFreq_p(msSpWindow, MSSpectralWindow:: columnName(MSSpectralWindow::CHAN_FREQ)), chanWidth_p(msSpWindow, MSSpectralWindow:: columnName(MSSpectralWindow::CHAN_WIDTH)), effectiveBW_p(msSpWindow, MSSpectralWindow:: columnName(MSSpectralWindow::EFFECTIVE_BW)), flagRow_p(msSpWindow, MSSpectralWindow:: columnName(MSSpectralWindow::FLAG_ROW)), freqGroup_p(msSpWindow, MSSpectralWindow:: columnName(MSSpectralWindow::FREQ_GROUP)), freqGroupName_p(msSpWindow, MSSpectralWindow:: columnName(MSSpectralWindow::FREQ_GROUP_NAME)), ifConvChain_p(msSpWindow, MSSpectralWindow:: columnName(MSSpectralWindow::IF_CONV_CHAIN)), measFreqRef_p(msSpWindow, MSSpectralWindow:: columnName(MSSpectralWindow::MEAS_FREQ_REF)), name_p(msSpWindow, MSSpectralWindow:: columnName(MSSpectralWindow::NAME)), netSideband_p(msSpWindow, MSSpectralWindow:: columnName(MSSpectralWindow::NET_SIDEBAND)), numChan_p(msSpWindow, MSSpectralWindow:: columnName(MSSpectralWindow::NUM_CHAN)), refFrequency_p(msSpWindow, MSSpectralWindow:: columnName(MSSpectralWindow::REF_FREQUENCY)), resolution_p(msSpWindow, MSSpectralWindow:: columnName(MSSpectralWindow::RESOLUTION)), totalBandwidth_p(msSpWindow, MSSpectralWindow:: columnName(MSSpectralWindow::TOTAL_BANDWIDTH)), assocNature_p(), assocSpwId_p(), bbcNo_p(), bbcSideband_p(), dopplerId_p(), receiverId_p(), chanFreqMeas_p(msSpWindow, MSSpectralWindow:: columnName(MSSpectralWindow::CHAN_FREQ)), refFrequencyMeas_p(msSpWindow, MSSpectralWindow:: columnName(MSSpectralWindow::REF_FREQUENCY)), chanFreqQuant_p(msSpWindow, MSSpectralWindow:: columnName(MSSpectralWindow::CHAN_FREQ)), chanWidthQuant_p(msSpWindow, MSSpectralWindow:: columnName(MSSpectralWindow::CHAN_WIDTH)), effectiveBWQuant_p(msSpWindow, MSSpectralWindow:: columnName(MSSpectralWindow::EFFECTIVE_BW)), refFrequencyQuant_p(msSpWindow, MSSpectralWindow:: columnName(MSSpectralWindow::REF_FREQUENCY)), resolutionQuant_p(msSpWindow, MSSpectralWindow:: columnName(MSSpectralWindow::RESOLUTION)), totalBandwidthQuant_p(msSpWindow, MSSpectralWindow:: columnName(MSSpectralWindow::TOTAL_BANDWIDTH)) { attachOptionalCols(msSpWindow); } MSSpWindowColumns::~MSSpWindowColumns() {} MSSpWindowColumns::MSSpWindowColumns(): ROMSSpWindowColumns(), chanFreq_p(), chanWidth_p(), effectiveBW_p(), flagRow_p(), freqGroup_p(), freqGroupName_p(), ifConvChain_p(), measFreqRef_p(), name_p(), netSideband_p(), numChan_p(), refFrequency_p(), resolution_p(), totalBandwidth_p(), assocNature_p(), assocSpwId_p(), bbcNo_p(), bbcSideband_p(), dopplerId_p(), receiverId_p(), chanFreqMeas_p(), refFrequencyMeas_p(), chanFreqQuant_p(), chanWidthQuant_p(), effectiveBWQuant_p(), refFrequencyQuant_p(), resolutionQuant_p(), totalBandwidthQuant_p() { } void MSSpWindowColumns::attach(MSSpectralWindow& msSpWindow) { ROMSSpWindowColumns::attach(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(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); } // Local Variables: // compile-command: "gmake MSSpWindowColumns" // End: } //# NAMESPACE CASACORE - END casacore-2.4.1/ms/MeasurementSets/MSSpWindowColumns.h000066400000000000000000000450201321422335000225610ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 read-only access to MSASpectralWindow columns // // // // // //
      • MSSpectralWindow //
      • ArrayColumn //
      • ScalarColumn // // // // ROMSSpectralWindowColumns stands for Read-Only MeasurementSet SpectralWindow Table columns. // // // // This class provides read-only 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 ROMSColumns for an example. // // // // See MSColumns for the motivation. // class ROMSSpWindowColumns { public: // Create a columns object that accesses the data in the specified Table ROMSSpWindowColumns(const MSSpectralWindow& msSpWindow); // The destructor does nothing special ~ROMSSpWindowColumns(); // Access to columns // const ROArrayColumn& chanFreq() const {return chanFreq_p;} const ROArrayQuantColumn& chanFreqQuant() const { return chanFreqQuant_p;} const ROArrayMeasColumn& chanFreqMeas() const { return chanFreqMeas_p;} const ROArrayColumn& chanWidth() const {return chanWidth_p;} const ROArrayQuantColumn& chanWidthQuant() const { return chanWidthQuant_p;} const ROArrayColumn& effectiveBW() const {return effectiveBW_p;} const ROArrayQuantColumn& effectiveBWQuant() const { return effectiveBWQuant_p;} const ROScalarColumn& freqGroup() const {return freqGroup_p;} const ROScalarColumn& freqGroupName() const {return freqGroupName_p;} const ROScalarColumn& ifConvChain() const {return ifConvChain_p;} const ROScalarColumn& flagRow() const {return flagRow_p;} const ROScalarColumn& measFreqRef() const {return measFreqRef_p;} const ROScalarColumn& name() const {return name_p;} const ROScalarColumn& netSideband() const {return netSideband_p;} const ROScalarColumn& numChan() const {return numChan_p;} const ROScalarColumn& refFrequency() const {return refFrequency_p;} const ROScalarQuantColumn& refFrequencyQuant() const { return refFrequencyQuant_p;} const ROScalarMeasColumn& refFrequencyMeas() const { return refFrequencyMeas_p;} const ROArrayColumn& resolution() const {return resolution_p;} const ROArrayQuantColumn& resolutionQuant() const { return resolutionQuant_p;} const ROScalarColumn& totalBandwidth() const { return totalBandwidth_p;} const ROScalarQuantColumn& totalBandwidthQuant() const { return totalBandwidthQuant_p;} // // Access to optional columns // const ROArrayColumn& assocNature() const {return assocNature_p;} const ROArrayColumn& assocSpwId() const {return assocSpwId_p;} const ROScalarColumn& bbcNo() const {return bbcNo_p;} const ROScalarColumn& bbcSideband() const {return bbcSideband_p;} const ROScalarColumn& dopplerId() const {return dopplerId_p;} const ROScalarColumn& receiverId() const {return receiverId_p;} // // Convenience function that returns the number of rows in any of the columns uInt 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. Int matchSpw(const MFrequency& refFreq, uInt nChan, const Quantum& bandwidth, Int ifChain, const Quantum& tolerance, Int tryRow=-1) const; // Similar to above, but also pass in the frame info. Int 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, Int 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. Vector 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 Int 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. ROMSSpWindowColumns(); //# 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. ROMSSpWindowColumns(const ROMSSpWindowColumns&); ROMSSpWindowColumns& operator=(const ROMSSpWindowColumns&); //# 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(uInt row, MFrequency::Types refType, Double refFreqInHz, Double tolInHz) const; Bool matchRefFreqCnvtrd(uInt row, MFrequency refOrChanFreq, const Bool isRefFreq, const MeasFrame& measFrm, const MSDopplerColumns& msdopc, const MSSourceColumns& mssrcc, Double tolInHz) const; Bool matchChanFreq(uInt row, const Vector& chanFreqInHz, Double tolInHz) const; Bool matchIfConvChain(uInt row, Int ifChain) const; Bool matchTotalBandwidth(uInt row, Double bandwidthInHz, Double tolInHz) const; Bool matchNumChan(uInt row, Int nChan) const; // //# required columns ROArrayColumn chanFreq_p; ROArrayColumn chanWidth_p; ROArrayColumn effectiveBW_p; ROScalarColumn flagRow_p; ROScalarColumn freqGroup_p; ROScalarColumn freqGroupName_p; ROScalarColumn ifConvChain_p; ROScalarColumn measFreqRef_p; ROScalarColumn name_p; ROScalarColumn netSideband_p; ROScalarColumn numChan_p; ROScalarColumn refFrequency_p; ROArrayColumn resolution_p; ROScalarColumn totalBandwidth_p; //# optional columns ROArrayColumn assocNature_p; ROArrayColumn assocSpwId_p; ROScalarColumn bbcNo_p; ROScalarColumn bbcSideband_p; ROScalarColumn dopplerId_p; ROScalarColumn receiverId_p; //# Access to Measure columns ROArrayMeasColumn chanFreqMeas_p; ROScalarMeasColumn refFrequencyMeas_p; //# Access to Quantum columns ROArrayQuantColumn chanFreqQuant_p; ROArrayQuantColumn chanWidthQuant_p; ROArrayQuantColumn effectiveBWQuant_p; ROScalarQuantColumn refFrequencyQuant_p; ROArrayQuantColumn resolutionQuant_p; ROScalarQuantColumn totalBandwidthQuant_p; // m_frame will be set from VLAFiller before calling matchSpw(), which is need when // converting MFrequency to a different frame. ( This did not work out! ) // MeasFrame* m_frame; }; // // A class to provide easy read-write 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 ROMSSpWindowColumns { public: // Create a columns object that accesses the data in the specified Table MSSpWindowColumns(MSSpectralWindow& msSpWindow); // The destructor does nothing special ~MSSpWindowColumns(); // Read-write 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;} // // Read-write 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;} // // Read-only access to required columns // const ROArrayColumn& chanFreq() const { return ROMSSpWindowColumns::chanFreq();} const ROArrayQuantColumn& chanFreqQuant() const { return ROMSSpWindowColumns::chanFreqQuant();} const ROArrayMeasColumn& chanFreqMeas() const { return ROMSSpWindowColumns::chanFreqMeas();} const ROArrayColumn& chanWidth() const { return ROMSSpWindowColumns::chanWidth();} const ROArrayQuantColumn& chanWidthQuant() const { return ROMSSpWindowColumns::chanWidthQuant();} const ROArrayColumn& effectiveBW() const { return ROMSSpWindowColumns::effectiveBW();} const ROArrayQuantColumn& effectiveBWQuant() const { return ROMSSpWindowColumns::effectiveBWQuant();} const ROScalarColumn& freqGroup() const { return ROMSSpWindowColumns::freqGroup();} const ROScalarColumn& freqGroupName() const { return ROMSSpWindowColumns::freqGroupName();} const ROScalarColumn& ifConvChain() const { return ROMSSpWindowColumns::ifConvChain();} const ROScalarColumn& flagRow() const { return ROMSSpWindowColumns::flagRow();} const ROScalarColumn& measFreqRef() const { return ROMSSpWindowColumns::measFreqRef();} const ROScalarColumn& name() const { return ROMSSpWindowColumns::name();} const ROScalarColumn& netSideband() const { return ROMSSpWindowColumns::netSideband();} const ROScalarColumn& numChan() const { return ROMSSpWindowColumns::numChan();} const ROScalarColumn& refFrequency() const { return ROMSSpWindowColumns::refFrequency();} const ROScalarQuantColumn& refFrequencyQuant() const { return ROMSSpWindowColumns::refFrequencyQuant();} const ROScalarMeasColumn& refFrequencyMeas() const { return ROMSSpWindowColumns::refFrequencyMeas();} const ROArrayColumn& resolution() const { return ROMSSpWindowColumns::resolution();} const ROArrayQuantColumn& resolutionQuant() const { return ROMSSpWindowColumns::resolutionQuant();} const ROScalarColumn& totalBandwidth() const { return ROMSSpWindowColumns::totalBandwidth();} const ROScalarQuantColumn& totalBandwidthQuant() const { return ROMSSpWindowColumns::totalBandwidthQuant();} // // Read-only access to optional columns // const ROArrayColumn& assocNature() const { return ROMSSpWindowColumns::assocNature();} const ROArrayColumn& assocSpwId() const { return ROMSSpWindowColumns::assocSpwId();} const ROScalarColumn& bbcNo() const { return ROMSSpWindowColumns::bbcNo();} const ROScalarColumn& bbcSideband() const { return ROMSSpWindowColumns::bbcSideband();} const ROScalarColumn& dopplerId() const { return ROMSSpWindowColumns::dopplerId();} const ROScalarColumn& receiverId() const { return ROMSSpWindowColumns::receiverId();} // 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(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(MSSpectralWindow& msSpWindow); //# 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; }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/ms/MeasurementSets/MSSpWindowEnums.h000066400000000000000000000116761321422335000222420ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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-2.4.1/ms/MeasurementSets/MSSpectralWindow.cc000066400000000000000000000202041321422335000225460ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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, uInt 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 &, uInt, 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; } void MSSpectralWindow::init() { if (! columnMap_p.ndefined()) { // the PredefinedColumns // // ASSOC_NATURE colMapDef(ASSOC_NATURE,"ASSOC_NATURE", TpArrayString, "Nature of association with other spectral window","",""); // ASSOC_SPW_ID colMapDef(ASSOC_SPW_ID,"ASSOC_SPW_ID",TpArrayInt, "Associated spectral window id","",""); // BBC_NO colMapDef(BBC_NO,"BBC_NO",TpInt, "Baseband converter number","",""); // BBC_SIDEBAND colMapDef(BBC_SIDEBAND,"BBC_SIDEBAND",TpInt, "BBC sideband","",""); // CHAN_FREQ colMapDef(CHAN_FREQ,"CHAN_FREQ", TpArrayDouble, "Center frequencies for each channel in the data matrix", "Hz","Frequency"); // CHAN_WIDTH colMapDef(CHAN_WIDTH,"CHAN_WIDTH",TpArrayDouble, "Channel width for each channel","Hz",""); // DOPPLER_ID colMapDef(DOPPLER_ID,"DOPPLER_ID",TpInt, "Doppler Id, points to DOPPLER table","",""); // EFFECTIVE_BW colMapDef(EFFECTIVE_BW,"EFFECTIVE_BW",TpArrayDouble, "Effective noise bandwidth of each channel","Hz",""); // FLAG_ROW colMapDef(FLAG_ROW,"FLAG_ROW",TpBool, "Row flag","",""); // FREQ_GROUP colMapDef(FREQ_GROUP,"FREQ_GROUP",TpInt, "Frequency group","",""); // FREQ_GROUP_NAME colMapDef(FREQ_GROUP_NAME,"FREQ_GROUP_NAME",TpString, "Frequency group name","",""); // IF_CONV_CHAIN colMapDef(IF_CONV_CHAIN, "IF_CONV_CHAIN", TpInt, "The IF conversion chain number","",""); // MEAS_FREQ_REF colMapDef(MEAS_FREQ_REF,"MEAS_FREQ_REF",TpInt, "Frequency Measure reference","",""); // NAME colMapDef(NAME,"NAME",TpString, "Spectral window name","",""); // NET_SIDEBAND colMapDef(NET_SIDEBAND,"NET_SIDEBAND",TpInt, "Net sideband","",""); // NUM_CHAN colMapDef(NUM_CHAN, "NUM_CHAN", TpInt, "Number of spectral channels","",""); // RECEIVER_ID colMapDef(RECEIVER_ID,"RECEIVER_ID",TpInt, "Receiver Id for this spectral window","",""); // REF_FREQUENCY colMapDef(REF_FREQUENCY, "REF_FREQUENCY", TpDouble, "The reference frequency", "Hz","Frequency"); // RESOLUTION colMapDef(RESOLUTION, "RESOLUTION", TpArrayDouble, "The effective noise bandwidth for each channel", "Hz",""); // TOTAL_BANDWIDTH colMapDef(TOTAL_BANDWIDTH, "TOTAL_BANDWIDTH", TpDouble, "The total bandwidth for this window","Hz",""); // PredefinedKeywords // init requiredTableDesc TableDesc requiredTD; // all required keywords uInt i; for (i = UNDEFINED_KEYWORD+1; i <= NUMBER_PREDEFINED_KEYWORDS; i++) { addKeyToDesc(requiredTD, PredefinedKeywords(i)); } // all required columns // set up the TableMeasure columns with variable reference // first add the variable ref column addColumnToDesc(requiredTD, MEAS_FREQ_REF); addColumnToDesc(requiredTD, CHAN_FREQ,1,"MEAS_FREQ_REF"); addColumnToDesc(requiredTD, REF_FREQUENCY,-1,"MEAS_FREQ_REF"); // define columns with known dimensionality addColumnToDesc(requiredTD, CHAN_WIDTH,1); addColumnToDesc(requiredTD, EFFECTIVE_BW,1); addColumnToDesc(requiredTD, RESOLUTION,1); for (i = UNDEFINED_COLUMN+1; i <= NUMBER_REQUIRED_COLUMNS; i++) { addColumnToDesc(requiredTD, PredefinedColumns(i)); } requiredTD_p=new TableDesc(requiredTD); } } MSSpectralWindow MSSpectralWindow::referenceCopy(const String& newTableName, const Block& writableColumns) const { return MSSpectralWindow(MSTable:: referenceCopy(newTableName,writableColumns)); } } //# NAMESPACE CASACORE - END casacore-2.4.1/ms/MeasurementSets/MSSpectralWindow.h000066400000000000000000000120661321422335000224170ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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, uInt 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 void init(); private: // required by the need to throw an exception in the destructor Bool hasBeenDestroyed_p; }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/ms/MeasurementSets/MSState.cc000066400000000000000000000132361321422335000206700ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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, uInt 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 &, uInt, 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; } void MSState::init() { if (! columnMap_p.ndefined()) { // the PredefinedColumns // CAL colMapDef(CAL,"CAL", TpDouble, "Noise calibration temperature","K",""); // FLAG_ROW colMapDef(FLAG_ROW,"FLAG_ROW", TpBool, "Row flag","",""); // LOAD colMapDef(LOAD,"LOAD", TpDouble, "Load temperature","K",""); // OBS_MODE colMapDef(OBS_MODE,"OBS_MODE", TpString, "Observing mode, e.g., OFF_SPECTRUM","",""); // REF colMapDef(REF,"REF", TpBool, "True for a reference observation","",""); // SIG colMapDef(SIG,"SIG", TpBool, "True for a source observation","",""); // SUB_SCAN colMapDef(SUB_SCAN,"SUB_SCAN", TpInt, "Sub scan number, relative to scan number","",""); // PredefinedKeywords // init requiredTableDesc TableDesc requiredTD; // all required keywords uInt i; for (i = UNDEFINED_KEYWORD+1; i <= NUMBER_PREDEFINED_KEYWORDS; i++) { addKeyToDesc(requiredTD, PredefinedKeywords(i)); } // all required columns for (i = UNDEFINED_COLUMN+1; i <= NUMBER_REQUIRED_COLUMNS; i++) { addColumnToDesc(requiredTD, PredefinedColumns(i)); } requiredTD_p=new TableDesc(requiredTD); } } MSState MSState::referenceCopy(const String& newTableName, const Block& writableColumns) const { return MSState(MSTable:: referenceCopy(newTableName,writableColumns)); } } //# NAMESPACE CASACORE - END casacore-2.4.1/ms/MeasurementSets/MSState.h000066400000000000000000000114651321422335000205340ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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, uInt 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 void init(); private: // required by the need to throw an exception in the destructor Bool hasBeenDestroyed_p; }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/ms/MeasurementSets/MSStateColumns.cc000066400000000000000000000135651321422335000222360ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN ROMSStateColumns::ROMSStateColumns(const MSState& msState): cal_p(msState, MSState::columnName(MSState::CAL)), flagRow_p(msState, MSState::columnName(MSState::FLAG_ROW)), load_p(msState, MSState::columnName(MSState::LOAD)), obsMode_p(msState, MSState::columnName(MSState::OBS_MODE)), ref_p(msState, MSState::columnName(MSState::REF)), sig_p(msState, MSState::columnName(MSState::SIG)), subScan_p(msState, MSState::columnName(MSState::SUB_SCAN)), calQuant_p(msState, MSState::columnName(MSState::CAL)), loadQuant_p(msState, MSState::columnName(MSState::LOAD)) {} ROMSStateColumns::~ROMSStateColumns() {} ROMSStateColumns::ROMSStateColumns(): cal_p(), flagRow_p(), load_p(), obsMode_p(), ref_p(), sig_p(), subScan_p(), calQuant_p(), loadQuant_p() {} void ROMSStateColumns::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)); } Int ROMSStateColumns::matchState(const Quantum& stateCalQ, const Quantum& stateLoadQ, const String& stateObsMode, const Bool& stateRef, const Bool& stateSig, const Int& stateSubScan, const Quantum& tolerance, Int tryRow){ uInt 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 uInt tr = tryRow; if (tr >= r) { throw(AipsError("ROMSStateColumns::matchState(...) - " "the row you suggest is too big")); } if (!flagRow()(tr) && fabs(calQuant()(tr).getValue(k) - calInK) < tolInK && 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) && fabs(calQuant()(r).getValue(k) - calInK) < tolInK && fabs(loadQuant()(r).getValue(k) - loadInK) < tolInK && obsMode()(r) == stateObsMode && ref()(r) == stateRef && sig()(r) == stateSig && subScan()(r) == stateSubScan) { return r; } } return -1; } MSStateColumns::MSStateColumns(MSState& msState): ROMSStateColumns(msState), cal_p(msState, MSState::columnName(MSState::CAL)), flagRow_p(msState, MSState::columnName(MSState::FLAG_ROW)), load_p(msState, MSState::columnName(MSState::LOAD)), obsMode_p(msState, MSState::columnName(MSState::OBS_MODE)), ref_p(msState, MSState::columnName(MSState::REF)), sig_p(msState, MSState::columnName(MSState::SIG)), subScan_p(msState, MSState::columnName(MSState::SUB_SCAN)), calQuant_p(msState, MSState::columnName(MSState::CAL)), loadQuant_p(msState, MSState::columnName(MSState::LOAD)) {} MSStateColumns::~MSStateColumns() {} MSStateColumns::MSStateColumns(): ROMSStateColumns(), cal_p(), flagRow_p(), load_p(), obsMode_p(), ref_p(), sig_p(), subScan_p(), calQuant_p(), loadQuant_p() {} void MSStateColumns::attach(MSState& msState) { ROMSStateColumns::attach(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)); } // Local Variables: // compile-command: "gmake MSStateColumns" // End: } //# NAMESPACE CASACORE - END casacore-2.4.1/ms/MeasurementSets/MSStateColumns.h000066400000000000000000000171561321422335000221000ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef MS_MSSTATECOLUMNS_H #define MS_MSSTATECOLUMNS_H #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN class MSState; // // A class to provide easy read-only access to MSState columns // // // // // //
      • MSState //
      • ScalarColumn // // // // ROMSStateColumns stands for Read-Only MeasurementSet State Table columns. // // // // This class provides read-only 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 ROMSColumns for an example. // // // // See MSColumns for the motivation. // class ROMSStateColumns { public: // Create a columns object that accesses the data in the specified Table ROMSStateColumns(const MSState& msState); // The destructor does nothing special ~ROMSStateColumns(); // Access to required columns // const ROScalarColumn& cal() const {return cal_p;} const ROScalarQuantColumn& calQuant() const { return calQuant_p;} const ROScalarColumn& flagRow() const {return flagRow_p;} const ROScalarColumn& load() const {return load_p;} const ROScalarQuantColumn& loadQuant() const { return loadQuant_p;} const ROScalarColumn& obsMode() const {return obsMode_p;} const ROScalarColumn& ref() const {return ref_p;} const ROScalarColumn& sig() const {return sig_p;} const ROScalarColumn& subScan() const {return subScan_p;} // // Convenience function that returns the number of rows in any of the columns uInt 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. Int matchState(const Quantum& stateCalQ, const Quantum& stateLoadQ, const String& stateObsMode, const Bool& stateRef, const Bool& stateSig, const Int& stateSubScan, const Quantum& tolerance, Int tryRow=-1); protected: //# default constructor creates a object that is not usable. Use the attach //# function correct this. ROMSStateColumns(); //# 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. ROMSStateColumns(const ROMSStateColumns&); ROMSStateColumns& operator=(const ROMSStateColumns&); //# required columns ROScalarColumn cal_p; ROScalarColumn flagRow_p; ROScalarColumn load_p; ROScalarColumn obsMode_p; ROScalarColumn ref_p; ROScalarColumn sig_p; ROScalarColumn subScan_p; //# Access to Quantum columns ROScalarQuantColumn calQuant_p; ROScalarQuantColumn loadQuant_p; }; // // A class to provide easy read-write 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 ROMSStateColumns { public: // Create a columns object that accesses the data in the specified Table MSStateColumns(MSState& msState); // The destructor does nothing special ~MSStateColumns(); // Read-write 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;} // // Read-only access to required columns // // 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(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; }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/ms/MeasurementSets/MSStateEnums.h000066400000000000000000000064051321422335000215420ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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-2.4.1/ms/MeasurementSets/MSSysCal.cc000066400000000000000000000175751321422335000210200ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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, uInt 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 &, uInt, 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; } void MSSysCal::init() { if (! columnMap_p.ndefined()) { // the PredefinedColumns // ANTENNA_ID colMapDef(ANTENNA_ID, "ANTENNA_ID", TpInt, "ID of antenna in this array","",""); // FEED_ID colMapDef(FEED_ID,"FEED_ID",TpInt, "Feed id","",""); // INTERVAL colMapDef(INTERVAL,"INTERVAL",TpDouble, "Interval for which this set of parameters is accurate", "s",""); // SPECTRAL_WINDOW_ID colMapDef(SPECTRAL_WINDOW_ID,"SPECTRAL_WINDOW_ID",TpInt, "ID for this spectral window setup","",""); // TIME colMapDef(TIME,"TIME",TpDouble, "Midpoint of time for which this set of " "parameters is accurate","s","Epoch"); // PHASE_DIFF colMapDef(PHASE_DIFF,"PHASE_DIFF",TpFloat, "Phase difference between receptor 2 and receptor 1", "rad",""); // PHASE_DIFF_FLAG colMapDef(PHASE_DIFF_FLAG,"PHASE_DIFF_FLAG",TpBool, "Flag for PHASE_DIFF","",""); // TANT colMapDef(TANT,"TANT",TpArrayFloat, "Antenna temperature for each receptor","K",""); // TANT_FLAG colMapDef(TANT_FLAG,"TANT_FLAG",TpBool, "Flag for TANT","",""); // TANT_SPECTRUM colMapDef(TANT_SPECTRUM,"TANT_SPECTRUM",TpArrayFloat, "Antenna temperature for each channel and receptor","K",""); // TANT_TSYS colMapDef(TANT_TSYS,"TANT_TSYS",TpArrayFloat, "Ratio of Antenna & system temperature for each receptor", "",""); // TANT_TSYS_FLAG colMapDef(TANT_TSYS_FLAG,"TANT_TSYS_FLAG",TpBool, "Flag for TANT_TSYS","",""); // TANT_TSYS_SPECTRUM colMapDef(TANT_TSYS_SPECTRUM,"TANT_TSYS_SPECTRUM",TpArrayFloat, "Ratio of Antenna & system temperature for each channel " "and receptor","",""); // TCAL colMapDef(TCAL,"TCAL",TpArrayFloat, "Calibration temperature for each receptor","K",""); // TCAL_FLAG colMapDef(TCAL_FLAG,"TCAL_FLAG",TpBool, "Flag for TCAL","",""); // TCAL_SPECTRUM colMapDef(TCAL_SPECTRUM,"TCAL_SPECTRUM",TpArrayFloat, "Calibration temperature for each channel and receptor","K",""); // TRX colMapDef(TRX,"TRX",TpArrayFloat, "Receiver temperature for each of the two receptors","K",""); // TRX_FLAG colMapDef(TRX_FLAG,"TRX_FLAG",TpBool, "Flag for TRX","",""); // TRX_SPECTRUM colMapDef(TRX_SPECTRUM,"TRX_SPECTRUM",TpArrayFloat, "Receiver temperature for each channel and receptor","K",""); // TSKY colMapDef(TSKY,"TSKY",TpArrayFloat, "Sky temperature for each of the two receptors","K",""); // TSKY_FLAG colMapDef(TSKY_FLAG,"TSKY_FLAG",TpBool, "Flag for TSKY","",""); // TSKY_SPECTRUM colMapDef(TSKY_SPECTRUM,"TSKY_SPECTRUM",TpArrayFloat, "Sky temperature for each channel and receptor","K",""); // TSYS colMapDef(TSYS,"TSYS",TpArrayFloat, "System temp. for each of the two receptors","K",""); // TSYS_FLAG colMapDef(TSYS_FLAG,"TSYS_FLAG",TpBool, "Flag for TSYS","",""); // TSYS_SPECTRUM colMapDef(TSYS_SPECTRUM,"TSYS_SPECTRUM",TpArrayFloat, "System temperature for each channel and receptor","K",""); // PredefinedKeywords // init requiredTableDesc TableDesc requiredTD; // all required keywords uInt i; for (i = UNDEFINED_KEYWORD+1; i <= NUMBER_PREDEFINED_KEYWORDS; i++) { addKeyToDesc(requiredTD, PredefinedKeywords(i)); } // all required columns for (i = UNDEFINED_COLUMN+1; i <= NUMBER_REQUIRED_COLUMNS; i++) { addColumnToDesc(requiredTD, PredefinedColumns(i)); } requiredTD_p=new TableDesc(requiredTD); } } MSSysCal MSSysCal::referenceCopy(const String& newTableName, const Block& writableColumns) const { return MSSysCal(MSTable::referenceCopy (newTableName,writableColumns)); } } //# NAMESPACE CASACORE - END casacore-2.4.1/ms/MeasurementSets/MSSysCal.h000066400000000000000000000115161321422335000206470ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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, uInt 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 void init(); private: // required by the need to throw an exception in the destructor Bool hasBeenDestroyed_p; }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/ms/MeasurementSets/MSSysCalColumns.cc000066400000000000000000000335421321422335000223510ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN ROMSSysCalColumns::ROMSSysCalColumns(const MSSysCal& msSysCal): isNull_p(True), antennaId_p(), feedId_p(), interval_p(), spectralWindowId_p(), time_p(), phaseDiff_p(), phaseDiffFlag_p(), tant_p(), tantFlag_p(), tantSpectrum_p(), tantTsys_p(), tantTsysFlag_p(), tantTsysSpectrum_p(), tcal_p(), tcalFlag_p(), tcalSpectrum_p(), trx_p(), trxFlag_p(), trxSpectrum_p(), tsky_p(), tskyFlag_p(), tskySpectrum_p(), tsys_p(), tsysFlag_p(), tsysSpectrum_p(), timeMeas_p(), intervalQuant_p(), timeQuant_p(), phaseDiffQuant_p(), tantQuant_p(), tantSpectrumQuant_p(), tcalQuant_p(), tcalSpectrumQuant_p(), trxQuant_p(), trxSpectrumQuant_p(), tskyQuant_p(), tskySpectrumQuant_p(), tsysQuant_p(), tsysSpectrumQuant_p() { attach(msSysCal); } ROMSSysCalColumns::~ROMSSysCalColumns() {} ROMSSysCalColumns::ROMSSysCalColumns(): isNull_p(True), antennaId_p(), feedId_p(), interval_p(), spectralWindowId_p(), time_p(), phaseDiff_p(), phaseDiffFlag_p(), tant_p(), tantFlag_p(), tantSpectrum_p(), tantTsys_p(), tantTsysFlag_p(), tantTsysSpectrum_p(), tcal_p(), tcalFlag_p(), tcalSpectrum_p(), trx_p(), trxFlag_p(), trxSpectrum_p(), tsky_p(), tskyFlag_p(), tskySpectrum_p(), tsys_p(), tsysFlag_p(), tsysSpectrum_p(), timeMeas_p(), intervalQuant_p(), timeQuant_p(), phaseDiffQuant_p(), tantQuant_p(), tantSpectrumQuant_p(), tcalQuant_p(), tcalSpectrumQuant_p(), trxQuant_p(), trxSpectrumQuant_p(), tskyQuant_p(), tskySpectrumQuant_p(), tsysQuant_p(), tsysSpectrumQuant_p() { } void ROMSSysCalColumns::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); } } } MSSysCalColumns::MSSysCalColumns(MSSysCal& msSysCal): ROMSSysCalColumns(), antennaId_p(), feedId_p(), interval_p(), spectralWindowId_p(), time_p(), phaseDiff_p(), phaseDiffFlag_p(), tant_p(), tantFlag_p(), tantSpectrum_p(), tantTsys_p(), tantTsysFlag_p(), tantTsysSpectrum_p(), tcal_p(), tcalFlag_p(), tcalSpectrum_p(), trx_p(), trxFlag_p(), trxSpectrum_p(), tsky_p(), tskyFlag_p(), tskySpectrum_p(), tsys_p(), tsysFlag_p(), tsysSpectrum_p(), timeMeas_p(), intervalQuant_p(), timeQuant_p(), phaseDiffQuant_p(), tantQuant_p(), tantSpectrumQuant_p(), tcalQuant_p(), tcalSpectrumQuant_p(), trxQuant_p(), trxSpectrumQuant_p(), tskyQuant_p(), tskySpectrumQuant_p(), tsysQuant_p(), tsysSpectrumQuant_p() { attach(msSysCal); } MSSysCalColumns::~MSSysCalColumns() {} void MSSysCalColumns::setEpochRef(MEpoch::Types ref, Bool tableMustBeEmpty) { timeMeas_p.setDescRefCode(ref, tableMustBeEmpty); } MSSysCalColumns::MSSysCalColumns(): ROMSSysCalColumns(), antennaId_p(), feedId_p(), interval_p(), spectralWindowId_p(), time_p(), phaseDiff_p(), phaseDiffFlag_p(), tant_p(), tantFlag_p(), tantSpectrum_p(), tantTsys_p(), tantTsysFlag_p(), tantTsysSpectrum_p(), tcal_p(), tcalFlag_p(), tcalSpectrum_p(), trx_p(), trxFlag_p(), trxSpectrum_p(), tsky_p(), tskyFlag_p(), tskySpectrum_p(), tsys_p(), tsysFlag_p(), tsysSpectrum_p(), timeMeas_p(), intervalQuant_p(), timeQuant_p(), phaseDiffQuant_p(), tantQuant_p(), tantSpectrumQuant_p(), tcalQuant_p(), tcalSpectrumQuant_p(), trxQuant_p(), trxSpectrumQuant_p(), tskyQuant_p(), tskySpectrumQuant_p(), tsysQuant_p(), tsysSpectrumQuant_p() { } void MSSysCalColumns::attach(MSSysCal& msSysCal) { ROMSSysCalColumns::attach(msSysCal); 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); } } } // Local Variables: // compile-command: "gmake MSSysCalColumns" // End: } //# NAMESPACE CASACORE - END casacore-2.4.1/ms/MeasurementSets/MSSysCalColumns.h000066400000000000000000000453071321422335000222150ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 read-only access to MSSysCal columns // // // // // //
      • MSSysCal //
      • ArrayColumn //
      • ScalarColumn // // // // ROMSSysCalColumns stands for Read-Only MeasurementSet SysCal Table // columns. // // // // This class provides read-only 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 ROMSColumns for an example. // // // // See MSColumns for the motivation. // class ROMSSysCalColumns { public: // Create a columns object that accesses the data in the specified Table ROMSSysCalColumns(const MSSysCal& msSysCal); // The destructor does nothing special ~ROMSSysCalColumns(); // Is this object defined? (MSSysCal table is optional) Bool isNull() const {return isNull_p;} // Access to columns // const ROScalarColumn& antennaId() const {return antennaId_p;} const ROScalarColumn& feedId() const {return feedId_p;} const ROScalarColumn& interval() const {return interval_p;} const ROScalarQuantColumn& intervalQuant() const { return intervalQuant_p;} const ROScalarColumn& spectralWindowId() const { return spectralWindowId_p;} const ROScalarColumn& time() const {return time_p;} const ROScalarQuantColumn& timeQuant() const {return timeQuant_p;} const ROScalarMeasColumn& timeMeas() const {return timeMeas_p;} // // Access to optional columns // const ROScalarColumn& phaseDiff() const {return phaseDiff_p;} const ROScalarQuantColumn& phaseDiffQuant() const { return phaseDiffQuant_p;} const ROScalarColumn& phaseDiffFlag() const {return phaseDiffFlag_p;} const ROArrayColumn& tant() const {return tant_p;} const ROArrayQuantColumn& tantQuant() const {return tantQuant_p;} const ROScalarColumn& tantFlag() const {return tantFlag_p;} const ROArrayColumn& tantSpectrum() const {return tantSpectrum_p;} const ROArrayQuantColumn& tantSpectrumQuant() const { return tantSpectrumQuant_p;} const ROArrayColumn& tantTsys() const {return tantTsys_p;} const ROScalarColumn& tantTsysFlag() const {return tantTsysFlag_p;} const ROArrayColumn& tantTsysSpectrum() const { return tantTsysSpectrum_p;} const ROArrayColumn& tcal() const {return tcal_p;} const ROArrayQuantColumn& tcalQuant() const {return tcalQuant_p;} const ROScalarColumn& tcalFlag() const {return tcalFlag_p;} const ROArrayColumn& tcalSpectrum() const {return tcalSpectrum_p;} const ROArrayQuantColumn& tcalSpectrumQuant() const { return tcalSpectrumQuant_p;} const ROArrayColumn& trx() const {return trx_p;} const ROArrayQuantColumn& trxQuant() const {return trxQuant_p;} const ROScalarColumn& trxFlag() const {return trxFlag_p;} const ROArrayColumn& trxSpectrum() const {return trxSpectrum_p;} const ROArrayQuantColumn& trxSpectrumQuant() const { return trxSpectrumQuant_p;} const ROArrayColumn& tsky() const {return tsky_p;} const ROArrayQuantColumn& tskyQuant() const {return tskyQuant_p;} const ROScalarColumn& tskyFlag() const {return tskyFlag_p;} const ROArrayColumn& tskySpectrum() const {return tskySpectrum_p;} const ROArrayQuantColumn& tskySpectrumQuant() const { return tskySpectrumQuant_p;} const ROArrayColumn& tsys() const {return tsys_p;} const ROArrayQuantColumn& tsysQuant() const {return tsysQuant_p;} const ROScalarColumn& tsysFlag() const {return tsysFlag_p;} const ROArrayColumn& tsysSpectrum() const {return tsysSpectrum_p;} const ROArrayQuantColumn& 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. uInt nrow() const {return isNull() ? 0 : antennaId_p.nrow();} protected: //# default constructor creates a object that is not usable. Use the attach //# function correct this. ROMSSysCalColumns(); //# 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. ROMSSysCalColumns(const ROMSSysCalColumns&); ROMSSysCalColumns& operator=(const ROMSSysCalColumns&); //# 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 ROScalarColumn antennaId_p; ROScalarColumn feedId_p; ROScalarColumn interval_p; ROScalarColumn spectralWindowId_p; ROScalarColumn time_p; //# optional columns ROScalarColumn phaseDiff_p; ROScalarColumn phaseDiffFlag_p; ROArrayColumn tant_p; ROScalarColumn tantFlag_p; ROArrayColumn tantSpectrum_p; ROArrayColumn tantTsys_p; ROScalarColumn tantTsysFlag_p; ROArrayColumn tantTsysSpectrum_p; ROArrayColumn tcal_p; ROScalarColumn tcalFlag_p; ROArrayColumn tcalSpectrum_p; ROArrayColumn trx_p; ROScalarColumn trxFlag_p; ROArrayColumn trxSpectrum_p; ROArrayColumn tsky_p; ROScalarColumn tskyFlag_p; ROArrayColumn tskySpectrum_p; ROArrayColumn tsys_p; ROScalarColumn tsysFlag_p; ROArrayColumn tsysSpectrum_p; //# Access to Measure columns ROScalarMeasColumn timeMeas_p; //# Access to Quantum columns ROScalarQuantColumn intervalQuant_p; ROScalarQuantColumn timeQuant_p; //# Optional Quantum columns ROScalarQuantColumn phaseDiffQuant_p; ROArrayQuantColumn tantQuant_p; ROArrayQuantColumn tantSpectrumQuant_p; ROArrayQuantColumn tcalQuant_p; ROArrayQuantColumn tcalSpectrumQuant_p; ROArrayQuantColumn trxQuant_p; ROArrayQuantColumn trxSpectrumQuant_p; ROArrayQuantColumn tskyQuant_p; ROArrayQuantColumn tskySpectrumQuant_p; ROArrayQuantColumn tsysQuant_p; ROArrayQuantColumn tsysSpectrumQuant_p; }; // // A class to provide easy read-write 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 ROMSSysCalColumns { public: // Create a columns object that accesses the data in the specified Table MSSysCalColumns(MSSysCal& msSysCal); // The destructor does nothing special ~MSSysCalColumns(); // Read-write 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;} // // Read-write 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;} // // Read-only access to required columns // const ROScalarColumn& antennaId() const { return ROMSSysCalColumns::antennaId();} const ROScalarColumn& feedId() const { return ROMSSysCalColumns::feedId();} const ROScalarColumn& interval() const { return ROMSSysCalColumns::interval();} const ROScalarQuantColumn& intervalQuant() const { return ROMSSysCalColumns::intervalQuant();} const ROScalarColumn& spectralWindowId() const { return ROMSSysCalColumns::spectralWindowId();} const ROScalarColumn& time() const { return ROMSSysCalColumns::time();} const ROScalarQuantColumn& timeQuant() const { return ROMSSysCalColumns::timeQuant();} const ROScalarMeasColumn& timeMeas() const { return ROMSSysCalColumns::timeMeas();} // // Read-only access to optional columns // const ROScalarColumn& phaseDiff() const { return ROMSSysCalColumns::phaseDiff();} const ROScalarQuantColumn& phaseDiffQuant() const { return ROMSSysCalColumns::phaseDiffQuant();} const ROScalarColumn& phaseDiffFlag() const { return ROMSSysCalColumns::phaseDiffFlag();} const ROArrayColumn& tant() const { return ROMSSysCalColumns::tant();} const ROArrayQuantColumn& tantQuant() const { return ROMSSysCalColumns::tantQuant();} const ROScalarColumn& tantFlag() const { return ROMSSysCalColumns::tantFlag();} const ROArrayColumn& tantSpectrum() const { return ROMSSysCalColumns::tantSpectrum();} const ROArrayQuantColumn& tantSpectrumQuant() const { return ROMSSysCalColumns::tantSpectrumQuant();} const ROArrayColumn& tantTsys() const { return ROMSSysCalColumns::tantTsys();} const ROScalarColumn& tantTsysFlag() const { return ROMSSysCalColumns::tantTsysFlag();} const ROArrayColumn& tantTsysSpectrum() const { return ROMSSysCalColumns::tantTsysSpectrum();} const ROArrayColumn& tcal() const { return ROMSSysCalColumns::tcal();} const ROArrayQuantColumn& tcalQuant() const { return ROMSSysCalColumns::tcalQuant();} const ROScalarColumn& tcalFlag() const { return ROMSSysCalColumns::tcalFlag();} const ROArrayColumn& tcalSpectrum() const { return ROMSSysCalColumns::tcalSpectrum();} const ROArrayQuantColumn& tcalSpectrumQuant() const { return ROMSSysCalColumns::tcalSpectrumQuant();} const ROArrayColumn& trx() const {return ROMSSysCalColumns::trx();} const ROArrayQuantColumn& trxQuant() const { return ROMSSysCalColumns::trxQuant();} const ROScalarColumn& trxFlag() const { return ROMSSysCalColumns::trxFlag();} const ROArrayColumn& trxSpectrum() const { return ROMSSysCalColumns::trxSpectrum();} const ROArrayQuantColumn& trxSpectrumQuant() const { return ROMSSysCalColumns::trxSpectrumQuant();} const ROArrayColumn& tsky() const { return ROMSSysCalColumns::tsky();} const ROArrayQuantColumn& tskyQuant() const { return ROMSSysCalColumns::tskyQuant();} const ROScalarColumn& tskyFlag() const { return ROMSSysCalColumns::tskyFlag();} const ROArrayColumn& tskySpectrum() const { return ROMSSysCalColumns::tskySpectrum();} const ROArrayQuantColumn& tskySpectrumQuant() const { return ROMSSysCalColumns::tskySpectrumQuant();} const ROArrayColumn& tsys() const { return ROMSSysCalColumns::tsys();} const ROArrayQuantColumn& tsysQuant() const { return ROMSSysCalColumns::tsysQuant();} const ROScalarColumn& tsysFlag() const { return ROMSSysCalColumns::tsysFlag();} const ROArrayColumn& tsysSpectrum() const { return ROMSSysCalColumns::tsysSpectrum();} const ROArrayQuantColumn& tsysSpectrumQuant() const { return ROMSSysCalColumns::tsysSpectrumQuant();} // // 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(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(MSSysCal& msSysCal); //# 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; }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/ms/MeasurementSets/MSSysCalEnums.h000066400000000000000000000121231321422335000216520ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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-2.4.1/ms/MeasurementSets/MSTable.cc000066400000000000000000000111321321422335000206300ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 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-2.4.1/ms/MeasurementSets/MSTable.h000066400000000000000000000274061321422335000205050ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id: MSTable.h 21451 2014-06-10 07:48:08Z gervandiepen $ #ifndef MS_MSTABLE_H #define MS_MSTABLE_H #include #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 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: // 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=""); // // 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 & tabDesc, 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()); // // Remove a column from a table // No exception is thrown if this invalidates the table // in order to permit more complex operations with invalid // intermediate states void removeColumn(const String & columnName) { Table::removeColumn(columnName); } // Remove columns from a table void removeColumn(const Vector& columnNames) { Table::removeColumn(columnNames); } // Rename a column // No exception is thrown if this invalidates the table // in order to permit more complex operations with invalid // intermediate states void renameColumn(const String & newName, const String & oldName) { Table::renameColumn(newName, oldName); } 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, uInt nrrow, Bool initialize); MSTable (SetupNewTable &newTab, const TableLock& lockOptions, uInt nrrow, Bool initialize); MSTable (const Table &table); MSTable (const MSTable &other); // ~MSTable(); // Assignment operator, reference semantics MSTable& operator=(const MSTable&); // These are the static ordered maps which contain the above info // ColEnum -> name static SimpleOrderedMap columnMap_p; // ColEnum -> DataType static SimpleOrderedMap colDTypeMap_p; // ColEnum -> comment string static SimpleOrderedMap colCommentMap_p; // ColEnum -> UNIT string static SimpleOrderedMap colUnitMap_p; // ColEnum -> MEASURE_TYPE string static SimpleOrderedMap colMeasureTypeMap_p; // KeyEnum -> name static SimpleOrderedMap keywordMap_p; // KeyEnum -> DataType static SimpleOrderedMap keyDTypeMap_p; // KeyEnum -> comment string static SimpleOrderedMap keyCommentMap_p; // The required TableDesc //# following fails in static initialization (segm. fault). // static TableDesc requiredTD_p; static CountedPtr requiredTD_p; // Define an entry in the column maps static void colMapDef(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(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; }; } //# NAMESPACE CASACORE - END //# #ifndef CASACORE_NO_AUTO_TEMPLATES //# #include //# #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-2.4.1/ms/MeasurementSets/MSTable.tcc000066400000000000000000000241541321422335000210240ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef MS_MSTABLE_TCC #define MS_MSTABLE_TCC #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# These statics cannot be compiled with egcs 1.0.3a. #if !defined(__GNUG__) || (defined(__GNUG__) && (__GNUG__ == 2) && (__GNUC_MINOR__ >= 91)) || defined(AIPS_GCC) template SimpleOrderedMap MSTable::columnMap_p(""); template SimpleOrderedMap MSTable::colDTypeMap_p(TpOther); template SimpleOrderedMap MSTable::colCommentMap_p(""); template SimpleOrderedMap MSTable::colUnitMap_p(""); template SimpleOrderedMap MSTable::colMeasureTypeMap_p(""); template SimpleOrderedMap MSTable::keywordMap_p(""); template SimpleOrderedMap MSTable::keyDTypeMap_p(TpOther); template SimpleOrderedMap MSTable::keyCommentMap_p(""); template CountedPtr MSTable::requiredTD_p; #endif 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, uInt nrrow, Bool initialize) : Table(MSTableImpl::setupCompression(newTab), nrrow, initialize) {} template MSTable::MSTable(SetupNewTable &newTab, const TableLock& lockOptions, uInt nrrow, Bool initialize) : Table(MSTableImpl::setupCompression(newTab), lockOptions, nrrow, initialize) {} 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 const String& MSTable::columnName(ColEnum which) { MSTableImpl::init(); return columnMap_p(which); } template ColEnum MSTable::columnType(const String &name) { MSTableImpl::init(); return ColEnum(MSTableImpl::mapType(columnMap_p,name)); } template DataType MSTable::columnDataType(ColEnum which) { MSTableImpl::init(); return DataType(colDTypeMap_p(which)); } template const String& MSTable::columnStandardComment(ColEnum which) { MSTableImpl::init(); return colCommentMap_p(which); } template const String& MSTable::columnUnit(ColEnum which) { MSTableImpl::init(); return colUnitMap_p(which); } template const String& MSTable::columnMeasureType(ColEnum which) { MSTableImpl::init(); return colMeasureTypeMap_p(which); } template const String& MSTable::keywordName(KeyEnum which) { MSTableImpl::init(); return keywordMap_p(which); } template KeyEnum MSTable::keywordType(const String &name) { MSTableImpl::init(); return KeyEnum(MSTableImpl::mapType(keywordMap_p,name)); } template DataType MSTable::keywordDataType(KeyEnum which) { MSTableImpl::init(); return DataType(keyDTypeMap_p(which)); } template const String& MSTable::keywordStandardComment(KeyEnum which) { MSTableImpl::init(); return keyCommentMap_p(which); } template Bool MSTable::isColumn(ColEnum which) const { return tableDesc().isColumn(columnName(which)); } template Bool MSTable::isColumnWritable(ColEnum which) const { return Table::isColumnWritable(columnName(which)); } template Bool MSTable::isKeyword(KeyEnum which) const { return keywordSet().isDefined(keywordName(which)); } template Bool MSTable::isScalar(ColEnum which) const { return tableDesc().columnDesc(columnName(which)).isScalar(); } template Bool MSTable::isArray(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, 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, 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::addKeyToDesc(TableDesc& td, KeyEnum key) { MSTableImpl::addKeyToDesc(td,keywordName(key),keywordDataType(key), keywordStandardComment(key)); } template void MSTable::addColumnCompression (TableDesc& td, ColEnum which, Bool autoScale, const String& type) { MSTableImpl::addColumnCompression (td, columnName(which), autoScale, type); } template void MSTable::colMapDef(ColEnum col, const String& colName, DataType colType, const String& colComment, const String& colUnit, const String& colMeasureType) { MSTableImpl::colMapDef(columnMap_p,colDTypeMap_p,colCommentMap_p, colUnitMap_p,colMeasureTypeMap_p,col,colName, colType,colComment,colUnit,colMeasureType); } template void MSTable::keyMapDef(KeyEnum key, const String& keyName, DataType keyType, const String& keyComment) { MSTableImpl::keyMapDef(keywordMap_p,keyDTypeMap_p,keyCommentMap_p, key,keyName,keyType,keyComment); } template Bool MSTable::validate(const TableDesc& tabDesc) { MSTableImpl::init(); return MSTableImpl::validate(tabDesc,*requiredTD_p); } template Bool MSTable::validate(const TableRecord& tabKeySet) { MSTableImpl::init(); return MSTableImpl::validate(tabKeySet,*requiredTD_p); } template const TableDesc& MSTable::requiredTableDesc() { MSTableImpl::init(); return *requiredTD_p; } template Table MSTable::referenceCopy(const String& newTableName, const Block& writableColumns) const { return MSTableImpl::referenceCopy(*this, newTableName, writableColumns); } } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/ms/MeasurementSets/MSTable2.cc000066400000000000000000000071031321422335000207150ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/ms/MeasurementSets/MSTableImpl.cc000066400000000000000000000502171321422335000214610ustar00rootroot00000000000000//# MSTableImpl.cc: the class that hold measurements from telescopes //# Copyright (C) 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include #include #include #include #include #include #include #include #include #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); } } Mutex MSTableImpl::initialized_mutex(Mutex::Recursive); Bool MSTableImpl::initialized_p(False); Int MSTableImpl::mapType(const SimpleOrderedMap& columnMap, const String &name) { // find first occurrance of name in the map (must be only occurrance) Int type = 0; //# 0=UNDEFINED_COLUMN for all enums for (uInt i=0; i=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.define (cdesc.name(), cname); newtab.adjustHypercolumns (old2new, True); } } return newtab; } void MSTableImpl::colMapDef(SimpleOrderedMap& columnMap, SimpleOrderedMap& colDTypeMap, SimpleOrderedMap& colCommentMap, SimpleOrderedMap& colUnitMap, SimpleOrderedMap& colMeasureTypeMap, Int col, const String& colName, Int colType, const String& colComment, const String& colUnit, const String& colMeasureType) { columnMap.define(col, colName); colDTypeMap.define(col, colType); colCommentMap.define(col, colComment); // no need to define these unless they are different from the // default, which is an empty string if (colUnit != "") colUnitMap.define(col, colUnit); if (colMeasureType != "") colMeasureTypeMap.define(col, colMeasureType); } void MSTableImpl::keyMapDef(SimpleOrderedMap& keywordMap, SimpleOrderedMap& keyDTypeMap, SimpleOrderedMap& keyCommentMap, Int key, const String& keyName, Int keyType, const String& keyComment) { keywordMap.define(key, keyName); keyDTypeMap.define(key, keyType); keyCommentMap.define(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 #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: // Convert a name to a ColEnum, static Int mapType(const SimpleOrderedMap& map, const String &name); // 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(SimpleOrderedMap& colMap, SimpleOrderedMap& colDTypeMap, SimpleOrderedMap& colCommentMap, SimpleOrderedMap& colUnitMap, SimpleOrderedMap& 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(SimpleOrderedMap& keyMap, SimpleOrderedMap& keyDTypeMap, SimpleOrderedMap& 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); // Initialize all MeasurementSet static mappings static void init(); private: static Mutex initialized_mutex; static Bool initialized_p; }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/ms/MeasurementSets/MSTileLayout.cc000066400000000000000000000070261321422335000217030ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/ms/MeasurementSets/MSTileLayout.h000066400000000000000000000112071321422335000215410ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/ms/MeasurementSets/MSWeather.cc000066400000000000000000000163071321422335000212110ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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, uInt 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 &, uInt, 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; } void MSWeather::init() { if (! columnMap_p.ndefined()) { // the PredefinedColumns // ANTENNA_ID colMapDef(ANTENNA_ID, "ANTENNA_ID", TpInt, "Antenna number","",""); // INTERVAL colMapDef(INTERVAL, "INTERVAL", TpDouble, "Interval over which data is relevant","s",""); // TIME colMapDef(TIME, "TIME", TpDouble, "An MEpoch specifying the midpoint of the time for" "which data is relevant","s","Epoch"); // DEW_POINT colMapDef(DEW_POINT, "DEW_POINT", TpFloat, "Dew point","K",""); // DEW_POINT_FLAG colMapDef(DEW_POINT_FLAG, "DEW_POINT_FLAG", TpBool, "Flag for dew point","",""); // H2O colMapDef(H2O, "H2O", TpFloat, "Average column density of water-vapor","m-2",""); // H2O_FLAG colMapDef(H2O_FLAG, "H2O_FLAG", TpBool, "Flag for average column density of water-vapor","",""); // IONOS_ELECTRON colMapDef(IONOS_ELECTRON, "IONOS_ELECTRON", TpFloat, "Average column density of electrons","m-2",""); // IONOS_ELECTRON_FLAG colMapDef(IONOS_ELECTRON_FLAG, "IONOS_ELECTRON_FLAG", TpBool, "Flag for average column density of electrons","",""); // PRESSURE colMapDef(PRESSURE, "PRESSURE", TpFloat, "Ambient atmospheric pressure","Pa",""); // PRESSURE_FLAG colMapDef(PRESSURE_FLAG, "PRESSURE_FLAG", TpBool, "Flag for ambient atmospheric pressure","",""); // REL_HUMIDITY colMapDef(REL_HUMIDITY, "REL_HUMIDITY", TpFloat, "Ambient relative humidity","%",""); // REL_HUMIDITY_FLAG colMapDef(REL_HUMIDITY_FLAG, "REL_HUMIDITY_FLAG", TpBool, "Flag for ambient relative humidity","",""); // TEMPERATURE colMapDef(TEMPERATURE, "TEMPERATURE", TpFloat, "Ambient Air Temperature for an antenna","K",""); // TEMPERATURE_FLAG colMapDef(TEMPERATURE_FLAG, "TEMPERATURE_FLAG", TpBool, "Flag for ambient Air Temperature for an antenna","",""); // WIND_DIRECTION colMapDef(WIND_DIRECTION, "WIND_DIRECTION", TpFloat, "Average wind direction","rad",""); // WIND_DIRECTION_FLAG colMapDef(WIND_DIRECTION_FLAG, "WIND_DIRECTION_FLAG", TpBool, "Flag for wind direction","",""); // WIND_SPEED colMapDef(WIND_SPEED, "WIND_SPEED", TpFloat, "Average wind speed","m/s",""); // WIND_SPEED_FLAG colMapDef(WIND_SPEED_FLAG, "WIND_SPEED_FLAG", TpBool, "Flag for wind speed","",""); // PredefinedKeywords // init requiredTableDesc TableDesc requiredTD; // all required keywords uInt i; for (i = UNDEFINED_KEYWORD+1; i <= NUMBER_PREDEFINED_KEYWORDS; i++) { addKeyToDesc(requiredTD, PredefinedKeywords(i)); } // all required columns for (i = UNDEFINED_COLUMN+1; i <= NUMBER_REQUIRED_COLUMNS; i++) { addColumnToDesc(requiredTD, PredefinedColumns(i)); } requiredTD_p=new TableDesc(requiredTD); } } MSWeather MSWeather::referenceCopy(const String& newTableName, const Block& writableColumns) const { return MSWeather(MSTable:: referenceCopy(newTableName,writableColumns)); } } //# NAMESPACE CASACORE - END casacore-2.4.1/ms/MeasurementSets/MSWeather.h000066400000000000000000000115611321422335000210500ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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, uInt 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 void init(); private: // required by the need to throw an exception in the destructor Bool hasBeenDestroyed_p; }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/ms/MeasurementSets/MSWeatherColumns.cc000066400000000000000000000303011321422335000225400ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN ROMSWeatherColumns::ROMSWeatherColumns(const MSWeather& msWeather): isNull_p(True), antennaId_p(), interval_p(), time_p(), dewPoint_p(), dewPointFlag_p(), H2O_p(), H2OFlag_p(), ionosElectron_p(), ionosElectronFlag_p(), pressure_p(), pressureFlag_p(), relHumidity_p(), relHumidityFlag_p(), temperature_p(), temperatureFlag_p(), windDirection_p(), windDirectionFlag_p(), windSpeed_p(), windSpeedFlag_p(), timeMeas_p(), intervalQuant_p(), timeQuant_p(), dewPointQuant_p(), H2OQuant_p(), ionosElectronQuant_p(), pressureQuant_p(), temperatureQuant_p(), windDirectionQuant_p(), windSpeedQuant_p() { attach(msWeather); } ROMSWeatherColumns::~ROMSWeatherColumns() {} ROMSWeatherColumns::ROMSWeatherColumns(): isNull_p(True), antennaId_p(), interval_p(), time_p(), dewPoint_p(), dewPointFlag_p(), H2O_p(), H2OFlag_p(), ionosElectron_p(), ionosElectronFlag_p(), pressure_p(), pressureFlag_p(), relHumidity_p(), relHumidityFlag_p(), temperature_p(), temperatureFlag_p(), windDirection_p(), windDirectionFlag_p(), windSpeed_p(), windSpeedFlag_p(), timeMeas_p(), intervalQuant_p(), timeQuant_p(), dewPointQuant_p(), H2OQuant_p(), ionosElectronQuant_p(), pressureQuant_p(), temperatureQuant_p(), windDirectionQuant_p(), windSpeedQuant_p() { } void ROMSWeatherColumns::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); } } MSWeatherColumns::MSWeatherColumns(MSWeather& msWeather): ROMSWeatherColumns(), antennaId_p(), interval_p(), time_p(), dewPoint_p(), dewPointFlag_p(), H2O_p(), H2OFlag_p(), ionosElectron_p(), ionosElectronFlag_p(), pressure_p(), pressureFlag_p(), relHumidity_p(), relHumidityFlag_p(), temperature_p(), temperatureFlag_p(), windDirection_p(), windDirectionFlag_p(), windSpeed_p(), windSpeedFlag_p(), timeMeas_p(), intervalQuant_p(), timeQuant_p(), dewPointQuant_p(), H2OQuant_p(), ionosElectronQuant_p(), pressureQuant_p(), temperatureQuant_p(), windDirectionQuant_p(), windSpeedQuant_p() { attach(msWeather); } MSWeatherColumns::~MSWeatherColumns() {} void MSWeatherColumns:: setEpochRef(MEpoch::Types ref, Bool tableMustBeEmpty) { timeMeas_p.setDescRefCode(ref, tableMustBeEmpty); } MSWeatherColumns::MSWeatherColumns(): ROMSWeatherColumns(), antennaId_p(), interval_p(), time_p(), dewPoint_p(), dewPointFlag_p(), H2O_p(), H2OFlag_p(), ionosElectron_p(), ionosElectronFlag_p(), pressure_p(), pressureFlag_p(), relHumidity_p(), relHumidityFlag_p(), temperature_p(), temperatureFlag_p(), windDirection_p(), windDirectionFlag_p(), windSpeed_p(), windSpeedFlag_p(), timeMeas_p(), intervalQuant_p(), timeQuant_p(), dewPointQuant_p(), H2OQuant_p(), ionosElectronQuant_p(), pressureQuant_p(), temperatureQuant_p(), windDirectionQuant_p(), windSpeedQuant_p() { } void MSWeatherColumns::attach(MSWeather& msWeather) { ROMSWeatherColumns::attach(msWeather); 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); } } } // Local Variables: // compile-command: "gmake MSWeatherColumns" // End: } //# NAMESPACE CASACORE - END casacore-2.4.1/ms/MeasurementSets/MSWeatherColumns.h000066400000000000000000000401411321422335000224050ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 read-only access to MSWeather columns // // // // // //
      • MSWeather //
      • ArrayColumn //
      • ScalarColumn // // // // ROMSWeatherColumns stands for Read-Only MeasurementSet Weather Table // columns. // // // // This class provides read-only 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 // ROMSColumns for an example. // // // // See MSColumns for the motivation. // class ROMSWeatherColumns { public: // Create a columns object that accesses the data in the specified Table ROMSWeatherColumns(const MSWeather& msWeather); // The destructor does nothing special ~ROMSWeatherColumns(); // Is this object defined? (MSWeather table is optional) Bool isNull() const {return isNull_p;} // Access to columns // const ROScalarColumn& antennaId() const {return antennaId_p;} const ROScalarColumn& interval() const {return interval_p;} const ROScalarQuantColumn& intervalQuant() const { return intervalQuant_p;} const ROScalarColumn& time() const {return time_p;} const ROScalarQuantColumn& timeQuant() const {return timeQuant_p;} const ROScalarMeasColumn& timeMeas() const {return timeMeas_p;} // // Access to optional columns // const ROScalarColumn& dewPoint() const {return dewPoint_p;} const ROScalarQuantColumn& dewPointQuant() const { return dewPointQuant_p;} const ROScalarColumn& dewPointFlag() const {return dewPointFlag_p;} const ROScalarColumn& H2O() const {return H2O_p;} const ROScalarColumn& H2OFlag() const {return H2OFlag_p;} const ROScalarQuantColumn& H2OQuant() const {return H2OQuant_p;} const ROScalarColumn& ionosElectron() const {return ionosElectron_p;} const ROScalarQuantColumn& ionosElectronQuant() const { return ionosElectronQuant_p;} const ROScalarColumn& ionosElectronFlag() const { return ionosElectronFlag_p;} const ROScalarColumn& pressure() const {return pressure_p;} const ROScalarQuantColumn& pressureQuant() const { return pressureQuant_p;} const ROScalarColumn& pressureFlag() const {return pressureFlag_p;} const ROScalarColumn& relHumidity() const {return relHumidity_p;} const ROScalarColumn& relHumidityFlag() const { return relHumidityFlag_p;} const ROScalarColumn& temperature() const {return temperature_p;} const ROScalarQuantColumn& temperatureQuant() const { return temperatureQuant_p;} const ROScalarColumn& temperatureFlag() const { return temperatureFlag_p;} const ROScalarColumn& windDirection() const {return windDirection_p;} const ROScalarQuantColumn& windDirectionQuant() const { return windDirectionQuant_p;} const ROScalarColumn& windDirectionFlag() const { return windDirectionFlag_p;} const ROScalarColumn& windSpeed() const {return windSpeed_p;} const ROScalarQuantColumn& windSpeedQuant() const { return windSpeedQuant_p;} const ROScalarColumn& 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. uInt nrow() const {return isNull() ? 0 : antennaId_p.nrow();} protected: //# default constructor creates a object that is not usable. Use the attach //# function correct this. ROMSWeatherColumns(); //# 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. ROMSWeatherColumns(const ROMSWeatherColumns&); ROMSWeatherColumns& operator=(const ROMSWeatherColumns&); //# 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 ROScalarColumn antennaId_p; ROScalarColumn interval_p; ROScalarColumn time_p; //# optional columns ROScalarColumn dewPoint_p; ROScalarColumn dewPointFlag_p; ROScalarColumn H2O_p; ROScalarColumn H2OFlag_p; ROScalarColumn ionosElectron_p; ROScalarColumn ionosElectronFlag_p; ROScalarColumn pressure_p; ROScalarColumn pressureFlag_p; ROScalarColumn relHumidity_p; ROScalarColumn relHumidityFlag_p; ROScalarColumn temperature_p; ROScalarColumn temperatureFlag_p; ROScalarColumn windDirection_p; ROScalarColumn windDirectionFlag_p; ROScalarColumn windSpeed_p; ROScalarColumn windSpeedFlag_p; //# Access to Measure columns ROScalarMeasColumn timeMeas_p; //# Access to Quantum columns ROScalarQuantColumn intervalQuant_p; ROScalarQuantColumn timeQuant_p; //# optional Quantum columns ROScalarQuantColumn dewPointQuant_p; ROScalarQuantColumn H2OQuant_p; ROScalarQuantColumn ionosElectronQuant_p; ROScalarQuantColumn pressureQuant_p; ROScalarQuantColumn temperatureQuant_p; ROScalarQuantColumn windDirectionQuant_p; ROScalarQuantColumn windSpeedQuant_p; }; // // A class to provide easy read-write 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 ROMSWeatherColumns { public: // Create a columns object that accesses the data in the specified Table MSWeatherColumns(MSWeather& msWeather); // The destructor does nothing special ~MSWeatherColumns(); // Read-write 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;} // // Read-write 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;} // // Read-only access to required columns // const ROScalarColumn& antennaId() const { return ROMSWeatherColumns::antennaId();} const ROScalarColumn& interval() const { return ROMSWeatherColumns::interval();} const ROScalarQuantColumn& intervalQuant() const { return ROMSWeatherColumns::intervalQuant();} const ROScalarColumn& time() const { return ROMSWeatherColumns::time();} const ROScalarQuantColumn& timeQuant() const { return ROMSWeatherColumns::timeQuant();} const ROScalarMeasColumn& timeMeas() const { return ROMSWeatherColumns::timeMeas();} // // Read-only access to optional columns // const ROScalarColumn& dewPoint() const { return ROMSWeatherColumns::dewPoint();} const ROScalarQuantColumn& dewPointQuant() const { return ROMSWeatherColumns::dewPointQuant();} const ROScalarColumn& dewPointFlag() const { return ROMSWeatherColumns::dewPointFlag();} const ROScalarColumn& H2O() const { return ROMSWeatherColumns::H2O();} const ROScalarColumn& H2OFlag() const { return ROMSWeatherColumns::H2OFlag();} const ROScalarQuantColumn& H2OQuant() const { return ROMSWeatherColumns::H2OQuant();} const ROScalarColumn& ionosElectron() const { return ROMSWeatherColumns::ionosElectron();} const ROScalarQuantColumn& ionosElectronQuant() const { return ROMSWeatherColumns::ionosElectronQuant();} const ROScalarColumn& ionosElectronFlag() const { return ROMSWeatherColumns::ionosElectronFlag();} const ROScalarColumn& pressure() const { return ROMSWeatherColumns::pressure();} const ROScalarQuantColumn& pressureQuant() const { return ROMSWeatherColumns::pressureQuant();} const ROScalarColumn& pressureFlag() const { return ROMSWeatherColumns::pressureFlag();} const ROScalarColumn& relHumidity() const { return ROMSWeatherColumns::relHumidity();} const ROScalarColumn& relHumidityFlag() const { return ROMSWeatherColumns::relHumidityFlag();} const ROScalarColumn& temperature() const { return ROMSWeatherColumns::temperature();} const ROScalarQuantColumn& temperatureQuant() const { return ROMSWeatherColumns::temperatureQuant();} const ROScalarColumn& temperatureFlag() const { return ROMSWeatherColumns::temperatureFlag();} const ROScalarColumn& windDirection() const { return ROMSWeatherColumns::windDirection();} const ROScalarQuantColumn& windDirectionQuant() const { return ROMSWeatherColumns::windDirectionQuant();} const ROScalarColumn& windDirectionFlag() const { return ROMSWeatherColumns::windDirectionFlag();} const ROScalarColumn& windSpeed() const { return ROMSWeatherColumns::windSpeed();} const ROScalarQuantColumn& windSpeedQuant() const { return ROMSWeatherColumns::windSpeedQuant();} const ROScalarColumn& windSpeedFlag() const { return ROMSWeatherColumns::windSpeedFlag();} // // 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(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(MSWeather& msWeather); //# 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; }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/ms/MeasurementSets/MSWeatherEnums.h000066400000000000000000000103401321422335000220520ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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-2.4.1/ms/MeasurementSets/MeasurementSet.cc000066400000000000000000001171461321422335000223160ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include #include #include #include #include #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, uInt 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 &, uInt, Bool) - " "table is not a valid MS")); } MeasurementSet::MeasurementSet(SetupNewTable &newTab, const TableLock& lockOptions, uInt 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 &, uInt, 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(); } MeasurementSet::MeasurementSet(const MeasurementSet &other) : MSTable(other), hasBeenDestroyed_p(False) { doNotLockSubtables_p = other.doNotLockSubtables_p; copySubtables (other); // others will be handled by initRefs mainLock_p=TableLock(TableLock::AutoNoReadLocking); // verify that other is valid if (&other != this) { addCat(); if (! validate(this->tableDesc())) throw (AipsError("MS(const MeasurementSet &) - " "MeasurementSet is not a valid MS")); } if (!isNull()){ initRefs(); } } MeasurementSet::~MeasurementSet() { // check to make sure that this MS 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 << "~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; copySubtables (other); hasBeenDestroyed_p=other.hasBeenDestroyed_p; 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; } void MeasurementSet::init() { if (! columnMap_p.ndefined()) { // the PredefinedColumns // ANTENNA1 colMapDef(ANTENNA1, "ANTENNA1", TpInt, "ID of first antenna in interferometer","",""); // ANTENNA2 colMapDef(ANTENNA2, "ANTENNA2", TpInt, "ID of second antenna in interferometer","",""); // ANTENNA3 colMapDef(ANTENNA3, "ANTENNA3", TpInt, "ID of third antenna in interferometer","",""); // ARRAY_ID colMapDef(ARRAY_ID, "ARRAY_ID", TpInt, "ID of array or subarray","",""); // BASELINE_REF colMapDef(BASELINE_REF,"BASELINE_REF",TpBool, "Reference antenna for this baseline, True for ANTENNA1","", ""); // the CORRECTED_DATA column, colMapDef(CORRECTED_DATA,"CORRECTED_DATA",TpArrayComplex, "The corrected data column","",""); // the DATA columns, colMapDef(DATA,"DATA",TpArrayComplex,"The data column","",""); // the DATA_DESC_ID colMapDef(DATA_DESC_ID,"DATA_DESC_ID",TpInt, "The data description table index","",""); // EXPOSURE colMapDef(EXPOSURE, "EXPOSURE", TpDouble, "The effective integration time","s",""); // FEED1 colMapDef(FEED1, "FEED1", TpInt, "The feed index for ANTENNA1","",""); // FEED2 colMapDef(FEED2, "FEED2", TpInt, "The feed index for ANTENNA2","",""); // FEED3 colMapDef(FEED3, "FEED3", TpInt, "The feed index for ANTENNA3","",""); // FIELD_ID colMapDef(FIELD_ID,"FIELD_ID", TpInt, "Unique id for this pointing","",""); // FLAG colMapDef(FLAG,"FLAG", TpArrayBool, "The data flags, array of bools with same shape as data","",""); // FLAG_CATEGORY colMapDef(FLAG_CATEGORY,"FLAG_CATEGORY", TpArrayBool, "The flag category, NUM_CAT flags for each datum","",""); // FLAG_ROW colMapDef(FLAG_ROW,"FLAG_ROW", TpBool, "Row flag - flag all data in this row if True","",""); // FLOAT_DATA colMapDef(FLOAT_DATA,"FLOAT_DATA",TpArrayFloat, "Floating point data - for single dish","",""); // IMAGING_WEIGHT colMapDef(IMAGING_WEIGHT,"IMAGING_WEIGHT",TpArrayFloat, "Weight set by imaging task (e.g. uniform weighting)","",""); // INTERVAL colMapDef(INTERVAL, "INTERVAL", TpDouble, "The sampling interval","s",""); // the LAG_DATA column, colMapDef(LAG_DATA,"LAG_DATA",TpArrayComplex, "The lag data column","",""); // the MODEL_DATA column, colMapDef(MODEL_DATA,"MODEL_DATA",TpArrayComplex, "The model data column","",""); // OBSERVATION_ID colMapDef(OBSERVATION_ID, "OBSERVATION_ID", TpInt, "ID for this observation, index in OBSERVATION table","",""); // PHASE_ID colMapDef(PHASE_ID,"PHASE_ID",TpInt, "Id for phase switching","",""); // PROCESSOR_ID colMapDef(PROCESSOR_ID,"PROCESSOR_ID",TpInt, "Id for backend processor, index in PROCESSOR table","",""); // PULSAR_BIN colMapDef(PULSAR_BIN, "PULSAR_BIN", TpInt, "Pulsar pulse-phase bin for this DATA","",""); // PULSAR_GATE_ID colMapDef(PULSAR_GATE_ID, "PULSAR_GATE_ID", TpInt, "ID for this gate, index into PULSAR_GATE table","",""); // SCAN_NUMBER colMapDef(SCAN_NUMBER, "SCAN_NUMBER", TpInt, "Sequential scan number from on-line system","",""); // STATE_ID colMapDef(STATE_ID,"STATE_ID",TpInt, "ID for this observing state","",""); // SIGMA colMapDef(SIGMA, "SIGMA", TpArrayFloat, "Estimated rms noise for channel with unity bandpass response","",""); // SIGMA_SPECTRUM colMapDef(SIGMA_SPECTRUM, "SIGMA_SPECTRUM", TpArrayFloat, "Estimated rms noise for each data point","",""); // TIME colMapDef(TIME, "TIME", TpDouble, "Modified Julian Day","s","Epoch"); // TIME_CENTROID colMapDef(TIME_CENTROID, "TIME_CENTROID", TpDouble, "Modified Julian Day","s","Epoch"); // TIME_EXTRA_PREC colMapDef(TIME_EXTRA_PREC, "TIME_EXTRA_PREC", TpDouble, "Additional precision for TIME","s",""); // UVW colMapDef(UVW, "UVW", TpArrayDouble, "Vector with uvw coordinates (in meters)","m","uvw"); // UVW2 colMapDef(UVW2,"UVW2",TpArrayDouble, "uvw coordinates for second pair of triple corr product", "m","uvw"); // VIDEO_POINT colMapDef(VIDEO_POINT,"VIDEO_POINT",TpArrayComplex, "zero frequency point, needed for transform to lag","",""); // WEIGHT colMapDef(WEIGHT, "WEIGHT", TpArrayFloat, "Weight for each polarization spectrum","",""); // WEIGHT_SPECTRUM colMapDef(WEIGHT_SPECTRUM, "WEIGHT_SPECTRUM", TpArrayFloat, "Weight for each data point","",""); // CORRECTED_WEIGHT_SPECTRUM colMapDef(CORRECTED_WEIGHT_SPECTRUM, "CORRECTED_WEIGHT_SPECTRUM", TpArrayFloat, "Weight for each corrected data point","",""); // PredefinedKeywords // ANTENNA keyMapDef(ANTENNA,"ANTENNA", TpTable, "Antenna subtable. Antenna positions, mount-types etc."); // DATA_DESCRIPTION keyMapDef(DATA_DESCRIPTION,"DATA_DESCRIPTION", TpTable, "DATA_DESCRIPTION subtable. Points to freq and pol layout" "in subtables"); // FEED keyMapDef(FEED,"FEED", TpTable, "Feed subtable. Responses, offsets, beams etc."); // FIELD keyMapDef(FIELD,"FIELD",TpTable, "Field subtable. Position etc. for each pointing."); // FLAG_CMD keyMapDef(FLAG_CMD,"FLAG_CMD",TpTable, "Flag command subtable. Stores global flagging commands"); // HISTORY keyMapDef(HISTORY,"HISTORY",TpTable, "Observation and processing history"); // MS_VERSION keyMapDef(MS_VERSION,"MS_VERSION",TpFloat, "MS version number, i.e., 2.0"); // OBSERVATION keyMapDef(OBSERVATION,"OBSERVATION",TpTable, "Observation subtable. Project, observer, schedule."); // POINTING keyMapDef(POINTING,"POINTING",TpTable, "Pointing subtable. Antenna pointing info."); // POLARIZATION keyMapDef(POLARIZATION,"POLARIZATION",TpTable, "Polarization set up subtable"); // PROCESSOR keyMapDef(PROCESSOR,"PROCESSOR",TpTable, "Backend Processor information subtable"); // SPECTRAL_WINDOW keyMapDef(SPECTRAL_WINDOW,"SPECTRAL_WINDOW",TpTable, "Spectral window subtable. Frequencies, bandwidths," " polarizations"); // STATE keyMapDef(STATE,"STATE",TpTable, "State subtable. State information (cal, ref etc.)"); // CAL_TABLES keyMapDef(CAL_TABLES,"CAL_TABLES",TpTable, "Associated calibration tables, one per row"); // DOPPLER keyMapDef(DOPPLER,"DOPPLER",TpTable, "Doppler tracking info"); // FREQ_OFFSET keyMapDef(FREQ_OFFSET,"FREQ_OFFSET",TpTable, "Frequency offset information"); // SORT_COLUMNS keyMapDef(SORT_COLUMNS,"SORT_COLUMNS",TpArrayString, "Listing of sort columns for each sorted table"); // SORT_ORDER keyMapDef(SORT_ORDER,"SORT_ORDER",TpArrayString, "Listing of sort orders for each sorted table"); // SORTED_TABLES keyMapDef(SORTED_TABLES,"SORTED_TABLES",TpTable, "Sorted reference tables of the main table, first one is" " main table"); // SOURCE keyMapDef(SOURCE,"SOURCE",TpTable, "Source subtable. Positions etc. for each source."); // SYSCAL keyMapDef(SYSCAL,"SYSCAL",TpTable, "SysCal subtable. System calibration data (Tsys etc.)."); // WEATHER keyMapDef(WEATHER,"WEATHER",TpTable, "Weather subtable. Weather info for each antenna."); // define required keywords and columns TableDesc requiredTD; // all required keywords uInt i; for (i = UNDEFINED_KEYWORD+1; i <= NUMBER_REQUIRED_KEYWORDS; i++) { addKeyToDesc(requiredTD, 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(requiredTD, UVW, shape, option); // Also define columns with Arrays with their correct dimensionality addColumnToDesc(requiredTD, FLAG, 2); addColumnToDesc(requiredTD, FLAG_CATEGORY, 3); addColumnToDesc(requiredTD, WEIGHT, 1); addColumnToDesc(requiredTD, SIGMA, 1); // Now define all other columns (duplicates are skipped) for (i = UNDEFINED_COLUMN+1; i <= NUMBER_REQUIRED_COLUMNS; i++) { addColumnToDesc(requiredTD, PredefinedColumns(i)); } // Add the column keyword for the FLAG_CATEGORY column requiredTD.rwColumnDesc("FLAG_CATEGORY").rwKeywordSet(). define("CATEGORY",Vector(0)); // init counted pointer to requiredTableDesc requiredTD_p=new TableDesc(requiredTD); } } 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); } } void MeasurementSet::createDefaultSubtables(Table::TableOption option) { SetupNewTable antennaSetup(antennaTableName(), MSAntenna::requiredTableDesc(),option); rwKeywordSet().defineTable(MS::keywordName(MS::ANTENNA), Table(antennaSetup)); SetupNewTable dataDescSetup(dataDescriptionTableName(), MSDataDescription::requiredTableDesc(),option); rwKeywordSet().defineTable(MS::keywordName(MS::DATA_DESCRIPTION), Table(dataDescSetup)); SetupNewTable feedSetup(feedTableName(), MSFeed::requiredTableDesc(),option); rwKeywordSet().defineTable(MS::keywordName(MS::FEED), Table(feedSetup)); SetupNewTable flagCmdSetup(flagCmdTableName(), MSFlagCmd::requiredTableDesc(),option); rwKeywordSet().defineTable(MS::keywordName(MS::FLAG_CMD), Table(flagCmdSetup)); SetupNewTable fieldSetup(fieldTableName(), MSField::requiredTableDesc(),option); rwKeywordSet().defineTable(MS::keywordName(MS::FIELD), Table(fieldSetup)); SetupNewTable historySetup(historyTableName(), MSHistory::requiredTableDesc(),option); rwKeywordSet().defineTable(MS::keywordName(MS::HISTORY), Table(historySetup)); SetupNewTable observationSetup(observationTableName(), MSObservation::requiredTableDesc(),option); rwKeywordSet().defineTable(MS::keywordName(MS::OBSERVATION), Table(observationSetup)); 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), Table(pointingSetup)); SetupNewTable polarizationSetup(polarizationTableName(), MSPolarization::requiredTableDesc(),option); rwKeywordSet().defineTable(MS::keywordName(MS::POLARIZATION), Table(polarizationSetup)); SetupNewTable processorSetup(processorTableName(), MSProcessor::requiredTableDesc(),option); rwKeywordSet().defineTable(MS::keywordName(MS::PROCESSOR), Table(processorSetup)); SetupNewTable spectralWindowSetup(spectralWindowTableName(), MSSpectralWindow::requiredTableDesc(),option); rwKeywordSet().defineTable(MS::keywordName(MS::SPECTRAL_WINDOW), Table(spectralWindowSetup)); SetupNewTable stateSetup(stateTableName(), MSState::requiredTableDesc(),option); rwKeywordSet().defineTable(MS::keywordName(MS::STATE), Table(stateSetup)); 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 (uInt 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(); OrderedMap > polMap=thisSelection.getPolMap(); OrderedMap > > 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-2.4.1/ms/MeasurementSets/MeasurementSet.h000066400000000000000000000471051321422335000221550ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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)); // uInt 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, uInt nrrow = 0, Bool initialize = False); MeasurementSet (SetupNewTable &newTab, const TableLock& lockOptions, uInt nrrow = 0, Bool initialize = False); MeasurementSet (const Table &table, const MeasurementSet * otherMs = NULL); 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); // Initialize the statics appropriately. This does not need to be // called by users, it is called by the implementation class // MSTableImpl. static void init(); // 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(); // 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-2.4.1/ms/MeasurementSets/StokesConverter.cc000066400000000000000000000345211321422335000225100ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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-2.4.1/ms/MeasurementSets/test/000077500000000000000000000000001321422335000200135ustar00rootroot00000000000000casacore-2.4.1/ms/MeasurementSets/test/CMakeLists.txt000066400000000000000000000011451321422335000225540ustar00rootroot00000000000000#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) target_link_libraries (${test} casa_ms) add_test (${test} ${CMAKE_SOURCE_DIR}/cmake/cmake_assay ./${test}) add_dependencies(check ${test}) endforeach (test) casacore-2.4.1/ms/MeasurementSets/test/antenna.params000066400000000000000000000014541321422335000226500ustar00rootroot00000000000000! 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-2.4.1/ms/MeasurementSets/test/feed.params000066400000000000000000000006471321422335000221320ustar00rootroot00000000000000! # 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-2.4.1/ms/MeasurementSets/test/field.params000066400000000000000000000002461321422335000223050ustar00rootroot00000000000000! # 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-2.4.1/ms/MeasurementSets/test/obs.params000066400000000000000000000001111321422335000217740ustar00rootroot00000000000000field.params spw.params antenna.params feed.params 10. 0.0 10000.0 100.0 casacore-2.4.1/ms/MeasurementSets/test/range-obs.params000066400000000000000000000001121321422335000230670ustar00rootroot00000000000000field.params spw.params antenna.params feed.params 10. 0.0 43200.0 7200.0 casacore-2.4.1/ms/MeasurementSets/test/selector-obs.params000066400000000000000000000001121321422335000236130ustar00rootroot00000000000000field.params spw.params antenna.params feed.params 10. 0.0 43200.0 7200.0 casacore-2.4.1/ms/MeasurementSets/test/spw.params000066400000000000000000000003751321422335000220360ustar00rootroot00000000000000! # 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-2.4.1/ms/MeasurementSets/test/tMSColumns.cc000066400000000000000000000211361321422335000223710ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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 ROMSColumns objects, testing all // declarations and column definitions for type consistency { MeasurementSet ms("tMSColumns_table.ms",Table::Old); ROMSColumns romsc(ms); } { MeasurementSet ms("tMSColumns_table.ms",Table::Update); MSColumns msc(ms); // remove the table ms.markForDelete(); } return 0; } catch (AipsError x) { cerr << x.getMesg() < #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 ROMSDataDescColumns& 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 ROMSDataDescColumns cols(table); // Check the data is still there getData(cols); } {// Delete the table MSDataDescription table(filename, Table::Old); table.markForDelete(); } } catch (AipsError x) { cerr << x.getMesg() << endl; cout << "FAIL" << endl; return 1; } cout << "OK" << endl; } // Local Variables: // compile-command: "gmake OPTLIB=1 XLIBLIST=0 tMSDataDescColumns" // End: casacore-2.4.1/ms/MeasurementSets/test/tMSFieldBuffer.cc000066400000000000000000000246461321422335000231370ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { // cerr << x.getMesg() << 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 (AipsError x) { // cerr << x.getMesg() << endl; // cout << "FAIL" << endl; // return 1; // } // cout << "OK" << endl; return 3; //untested } // Local Variables: // compile-command: "gmake OPTLIB=1 XLIBLIST=0 tMSFieldBuffer" // End: casacore-2.4.1/ms/MeasurementSets/test/tMSFieldBuffer.run000066400000000000000000000000421321422335000233360ustar00rootroot00000000000000#! /bin/sh echo "UNTESTED" exit 3 casacore-2.4.1/ms/MeasurementSets/test/tMSFieldEphem.cc000066400000000000000000000400741321422335000227550ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: CASA Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id: $ //# 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; } // add one row with ephemeris ms.field().addRow(); msfc.delayDir().put(1, dir); msfc.phaseDir().put(1, dir); msfc.referenceDir().put(1, dir); msfc.time().put(1,50802.708334*86400.); // start of the VTOP ephemeris msfc.ephemerisId().put(1,0); // ephemeris id 0 msfc.updateMeasComets(); { Int row = 1; 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 expected(Quantity(-54.3855, "deg"), Quantity(-19.8873, "deg"), MDirection::APP); MVDirection expDir(expected.getAngle()); AlwaysAssertExit(expDir.separation(MVDirection(dDir.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; MVDirection original(Quantity(305.6145129, "deg"), Quantity(-19.8873316, "deg")); 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()); // 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=" << ROScalarColumn(msIter.table(), "ANTENNA1")(0) << " a2=" << ROScalarColumn(msIter.table(), "ANTENNA2")(0) << " time=" << ROScalarColumn(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=" << ROScalarColumn(msIter.table(), "ANTENNA1")(0) << " a2=" << ROScalarColumn(msIter.table(), "ANTENNA2")(0) << " time=" << ROScalarColumn(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=" << ROScalarColumn(msIter.table(), "ANTENNA1")(0) << " a2=" << ROScalarColumn(msIter.table(), "ANTENNA2")(0) << " time=" << ROScalarColumn(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=" << ROScalarColumn(msIter1.table(), "ANTENNA1")(0) << " a2=" << ROScalarColumn(msIter1.table(), "ANTENNA2")(0) << " time=" << ROScalarColumn(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=" << ROScalarColumn(it->table(), "ANTENNA1")(0) << " a2=" << ROScalarColumn(it->table(), "ANTENNA2")(0) << " time=" << ROScalarColumn(it->table(), "TIME").getColumn() - 1e9 << endl; if (++i % 2 == 1) { msIter1 = msIter; it = &msIter1; } else { msIter = msIter1; it = &msIter; } } } int main (int argc, char* argv[]) { try { int nant = 3; int ntime = 5; double msinterval = 60.; double binwidth = 120.; if (argc > 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); } catch (std::exception& x) { cerr << "Unexpected exception: " << x.what() << endl; return 1; } return 0; } casacore-2.4.1/ms/MeasurementSets/test/tMSIter.out000066400000000000000000000065671321422335000221110ustar00rootroot00000000000000nrow=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] casacore-2.4.1/ms/MeasurementSets/test/tMSMainBuffer.cc000066400000000000000000000376611321422335000230010ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { // cerr << x.getMesg() << 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 (AipsError x) { // cerr << x.getMesg() << endl; // cout << "FAIL" << endl; // return 1; // } // cout << "OK" << endl; return 3; //untested } // Local Variables: // compile-command: "gmake OPTLIB=1 XLIBLIST=0 tMSMainBuffer" // End: casacore-2.4.1/ms/MeasurementSets/test/tMSMainBuffer.run000066400000000000000000000000421321422335000231770ustar00rootroot00000000000000#! /bin/sh echo "UNTESTED" exit 3 casacore-2.4.1/ms/MeasurementSets/test/tMSPolBuffer.cc000066400000000000000000000172211321422335000226350ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { // cerr << x.getMesg() << 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 (AipsError x) { // cerr << x.getMesg() << endl; // cout << "FAIL" << endl; // return 1; // } // cout << "OK" << endl; return 3; //untested } // Local Variables: // compile-command: "gmake OPTLIB=1 XLIBLIST=0 tMSPolBuffer" // End: casacore-2.4.1/ms/MeasurementSets/test/tMSPolBuffer.run000066400000000000000000000000421321422335000230450ustar00rootroot00000000000000#! /bin/sh echo "UNTESTED" exit 3 casacore-2.4.1/ms/MeasurementSets/test/tMeasurementSet.cc000066400000000000000000000432061321422335000234540ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: tMeasurementSet.cc 21451 2014-06-10 07:48:08Z gervandiepen $ //# 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=0;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)); for (Int i=0; i<10; i++) { Matrix arr(4,2); arr=Float(i); fldata.put(i,arr); } // 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 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 (AipsError x) { cerr << x.getMesg() << endl; } return 1; } casacore-2.4.1/ms/MeasurementSets/test/tStokesConverter.cc000066400000000000000000000076171321422335000236610ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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()) { Vector 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-2.4.1/ms/apps/readms.cc000066400000000000000000000201551321422335000164400ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: readms.cc 21451 2014-06-10 07:48:08Z gervandiepen $ //# Includes #include #include #include #include #include #include #include using namespace casacore; // Define the global variables shared between the main functions. bool myReadWeightSpectrum; bool myReadData; bool myReadFlag; bool myReadRowWise; bool myDoSinglePart; int myNPart; int myNPol; int myNChan; int myNTime; Vector myField; Vector mySpw; String myMsName; String myBaselines; 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("2017Oct14GvD"); 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 column?", "bool"); params.create ("flag", "true", "Read FLAG column?", "bool"); params.create ("weightspectrum", "false", "Read WEIGHT_SPECTRUM column?", "bool"); params.create ("fields", "", "Fields to read ", "int vector"); params.create ("ntime", "0", "Number of time steps to read (0=all)", "int"); params.create ("spw", "", "Spectral window numbers to read", "int vector"); params.create ("npol", "0", "Number of polarizations to read (0=all)", "int"); params.create ("nchan", "0", "Number of channels to read (0=all)", "int"); params.create ("baselines", "", "Baselines to read (CASA baseline selection)", "string"); params.create ("rowwise", "true", "Read the data row wise (thus a get per row)", "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; } myNChan = params.getInt ("nchan"); myNPol = params.getInt ("npol"); myNTime = params.getInt ("ntime"); mySpw = Vector(params.getIntArray ("spw")); myField = Vector(params.getIntArray ("fields")); 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; if (myNChan == 0) myNChan = 32768 * 32768; // very high number if (myNTime == 0) myNTime = 32768 * 32768; // Get remaining parameters. myBaselines = params.getString ("baselines"); myReadData = params.getBool ("data"); myReadFlag = params.getBool ("flag"); myReadWeightSpectrum = params.getBool ("weightspectrum"); myReadRowWise = params.getBool ("rowwise"); return true; } void showParms() { cout << "readms parameters:" << endl; cout << " nms = " << myNPart << " " << myMsName << endl; /* int nant = myAntPos.ncolumn(); cout << " nant = " << nant << " (nbaseline = "; if (myWriteAutoCorr) { cout << nant*(nant+1) / 2; } else { cout << nant*(nant-1) / 2; } cout << ')' << 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, myTileSizeFreq, myNPol, myNChan); int tileSize = tileShape.product() * 8 / 1024; cout << " tilesize = " << tileSize << " KBytes" << endl; if (!myWriteHDF5 && myUseMultiFile) { cout << " multi" << (myUseMultiFile==1 ? "file" : "hdf5"); int multiBlockSize = myMultiBlockSize; if (multiBlockSize <= 0) { multiBlockSize = tileSize; } cout << " (blocksize = " << multiBlockSize << " KBytes)" << endl; } */ } Int64 readTimeSteps (MeasurementSet& ms) { Int64 nrow = 0; Block itercol(2); itercol[0] = "TIME"; itercol[1] = "DATA_DESC_ID"; TableIterator iter(ms, itercol, TableIterator::Ascending, TableIterator::NoSort); int ntime = 0; while (!iter.pastEnd() && ntime < myNTime) { Table tab (iter.table()); if (myReadData) { ArrayColumn(tab, "DATA").getColumn(); } if (myReadFlag) { ArrayColumn(tab, "FLAG").getColumn(); } if (myReadWeightSpectrum) { ArrayColumn(tab, "WEIGHT_SPECTRUM").getColumn(); } nrow += tab.nrow(); iter.next(); ntime++; } return nrow; } void 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; if (msName.find ("%d") != String::npos) { name = String::format (msName.c_str(), seqnr); } else { name = msName; if (!myDoSinglePart) { name += String::format ("_p%d", seqnr); } } // Open the MS. Timer timer; MS ms(name); timer.show ("Opened MS " + name); timer.mark(); Int64 nrow = readTimeSteps (ms); timer.show ("Read " + String::toString(nrow) + " rows from MS " + msName); //if (seqnr == 0) { //msmaker->showCacheStatistics(); //} } void doAll() { #ifdef _OPENMP #pragma omp parallel for schedule(dynamic) #endif 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 #include #include #include #include #include #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; using namespace std; // This struct contains the data items needed to fill a spectral window in // the main table in HDF5. // It resembles the MSMainColumn class. struct HDF5Spw { CountedPtr spw; CountedPtr data; CountedPtr mdata; CountedPtr cdata; CountedPtr flag; CountedPtr weight; CountedPtr weightSpectrum; CountedPtr antenna1; CountedPtr antenna2; CountedPtr time; CountedPtr timeCentroid; CountedPtr interval; CountedPtr exposure; CountedPtr arrayId; CountedPtr fieldId; CountedPtr dataDescId; CountedPtr uvw; CountedPtr stateId; CountedPtr flagRow; CountedPtr feed1; CountedPtr feed2; CountedPtr processorId; CountedPtr scanNumber; CountedPtr observationId; CountedPtr sigma; }; // // 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 writeAutoCorr, bool calcUVW, bool writeWeightSpectrum, bool createImagerColumns, const Vector& npol, const Vector& nfreq, const Vector& startFreq, const Vector& stepFreq, int spw, int nspw, 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; 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 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 itsWriteAutoCorr; //# write autocorrelations? bool itsCalcUVW; //# calculate UVW coordinates? 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 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 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 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(); // //# Define the data. Int64 itsNrRow; CountedPtr 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 writeAutoCorr, bool calcUVW, bool writeWeightSpectrum, bool createImagerColumns, const Vector& npol, const Vector& nfreq, const Vector& startFreq, const Vector& stepFreq, int spw, int nspw, 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; itsWriteAutoCorr = writeAutoCorr; itsCalcUVW = calcUVW; 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); IncrementalStMan incrStMan("ISMData", ts); newTab.bindAll (incrStMan); StandardStMan stanStMan("SSMData", ts); newTab.bindColumn(MS::columnName(MS::ANTENNA1), stanStMan); newTab.bindColumn(MS::columnName(MS::ANTENNA2), 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); newTab.bindColumn(MS::columnName(MS::DATA), tiledData); } else { TiledShapeStMan tiledData("TiledData", itsDataTileShape); 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); } 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 = -C::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 (uInt i=0; i >& antuvw) { // Find the shape of the data array in each table row. IPosition shape(2, itsNPol[band], itsNFreq[band]); Array defFlags(shape, False); Array defData(shape); 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; jdata().put(itsNrRow, defData); itsMSCol->flag().put(itsNrRow, defFlags); if (itsWriteWeightSpectrum) { itsMSCol->weightSpectrum().put(itsNrRow, weightSpectrum); } itsMSCol->flagRow().put (itsNrRow, False); itsMSCol->time().put (itsNrRow, time); itsMSCol->antenna1().put (itsNrRow, j); itsMSCol->antenna2().put (itsNrRow, i); itsMSCol->feed1().put (itsNrRow, 0); itsMSCol->feed2().put (itsNrRow, 0); itsMSCol->dataDescId().put (itsNrRow, band); itsMSCol->processorId().put (itsNrRow, 0); itsMSCol->fieldId().put (itsNrRow, field); itsMSCol->interval().put (itsNrRow, itsStepTime); itsMSCol->exposure().put (itsNrRow, itsStepTime); itsMSCol->timeCentroid().put (itsNrRow, time); itsMSCol->scanNumber().put (itsNrRow, 0); itsMSCol->arrayId().put (itsNrRow, 0); itsMSCol->observationId().put (itsNrRow, 0); itsMSCol->stateId().put (itsNrRow, 0); itsMSCol->uvw().put (itsNrRow, myuvw); itsMSCol->weight().put (itsNrRow, weight); itsMSCol->sigma().put (itsNrRow, sigma); itsNrRow++; } } } void MSCreateCasa::writeTimeStepSpw (int band, int field, const vector >& antuvw) { int nrbasel = itsNrAnt*(itsNrAnt-1)/2; if (itsWriteAutoCorr) { nrbasel += itsNrAnt; } // 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; jdata().putColumnCells(rows, Array(shape)); itsMSCol->flag().putColumnCells(rows, Array(shape, False)); if (itsWriteWeightSpectrum) { itsMSCol->weightSpectrum().putColumnCells(rows, Array(shape, 1)); } itsMSCol->flagRow().putColumnCells (rows, Vector(nrbasel, False)); itsMSCol->time().putColumnCells (rows, times); itsMSCol->timeCentroid().putColumnCells (rows, times); times = itsStepTime; itsMSCol->interval().putColumnCells (rows, times); itsMSCol->exposure().putColumnCells (rows, times); itsMSCol->antenna1().putColumnCells (rows, vecint); itsMSCol->antenna2().putColumnCells (rows, vecint2); vecint = 0; itsMSCol->feed1().putColumnCells (rows, vecint); itsMSCol->feed2().putColumnCells (rows, vecint); itsMSCol->processorId().putColumnCells (rows, vecint); itsMSCol->scanNumber().putColumnCells (rows, vecint); itsMSCol->arrayId().putColumnCells (rows, vecint); itsMSCol->observationId().putColumnCells (rows, vecint); itsMSCol->stateId().putColumnCells (rows, vecint); vecint = band; itsMSCol->dataDescId().putColumnCells (rows, vecint); vecint = field; itsMSCol->fieldId().putColumnCells (rows, vecint); itsMSCol->uvw().putColumnCells (rows, myuvw); Matrix ones(itsNPol[band], nrbasel, 1); itsMSCol->weight().putColumnCells (rows, ones); itsMSCol->sigma().putColumnCells (rows, ones); itsNrRow += nrbasel; } void MSCreateCasa::addImagerColumns() { // Find data shape from DATA column. // Make tiles of appr. 1 MB. IPosition shape = ROTableColumn(itsMS, MS::columnName(MS::DATA)).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 { RODataManAccessor(itsMS, "TiledData", False).showCacheStatistics (cout); RODataManAccessor(itsMS, "SSMData", False).showCacheStatistics (cout); RODataManAccessor(itsMS, "ISMData", False).showCacheStatistics (cout); } MSCreateHDF5::MSCreateHDF5() : itsNrRow (0) {} MSCreateHDF5::~MSCreateHDF5() { } void MSCreateHDF5::createMS (const String& msName, int /*useMultiFile*/, int /*multiBlockSize*/, bool createImagerColumns, const String& /*flagColumn*/, int /*nflagBits*/) { Timer timer; // Create the file. itsFile = new HDF5File(msName, ByteIO::New); int nrbasel = itsNrAnt*(itsNrAnt-1)/2; if (itsWriteAutoCorr) { nrbasel += itsNrAnt; } // Create a group per spectral window. for (int band=itsSpw; bandextend (newShape3); itsSpws[band].flag->extend (newShape3); if (itsWriteWeightSpectrum) { itsSpws[band].weightSpectrum->extend (newShape3); } itsSpws[band].flagRow->extend (newShape1); itsSpws[band].time->extend (newShape1); itsSpws[band].timeCentroid->extend (newShape1); itsSpws[band].interval->extend (newShape1); itsSpws[band].exposure->extend (newShape1); itsSpws[band].antenna1->extend (newShape1); itsSpws[band].antenna2->extend (newShape1); itsSpws[band].feed1->extend (newShape1); itsSpws[band].feed2->extend (newShape1); itsSpws[band].processorId->extend (newShape1); itsSpws[band].scanNumber->extend (newShape1); itsSpws[band].arrayId->extend (newShape1); itsSpws[band].observationId->extend (newShape1); itsSpws[band].stateId->extend (newShape1); itsSpws[band].dataDescId->extend (newShape1); itsSpws[band].fieldId->extend (newShape1); itsSpws[band].weight->extend (newShape2); itsSpws[band].sigma->extend (newShape2); itsSpws[band].uvw->extend (newShapeu); } } void MSCreateHDF5::writeTimeStepSpw (int band, int field, const vector >& antuvw) { int nrbasel = itsNrAnt*(itsNrAnt-1)/2; if (itsWriteAutoCorr) { nrbasel += itsNrAnt; } // Get the time. Double time = itsStartTime + itsNrTimes*itsStepTime + itsStepTime/2; Vector times(nrbasel, time); // Fill ANTENNA1, ASNTENNA2 and UVW arrays. 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; jput (slicer3, Array(shape3)); itsSpws[band].flag->put (slicer3, Array(shape3, False)); if (itsWriteWeightSpectrum) { itsSpws[band].weightSpectrum->put (slicer3, Array(shape3, 1)); } itsSpws[band].flagRow->put (slicer1, Vector(nrbasel, False)); itsSpws[band].time->put (slicer1, times); itsSpws[band].timeCentroid->put (slicer1, times); times = itsStepTime; itsSpws[band].interval->put (slicer1, times); itsSpws[band].exposure->put (slicer1, times); itsSpws[band].antenna1->put (slicer1, vecint); itsSpws[band].antenna2->put (slicer1, vecint2); vecint = 0; itsSpws[band].feed1->put (slicer1, vecint); itsSpws[band].feed2->put (slicer1, vecint); itsSpws[band].processorId->put (slicer1, vecint); itsSpws[band].scanNumber->put (slicer1, vecint); itsSpws[band].arrayId->put (slicer1, vecint); itsSpws[band].observationId->put (slicer1, vecint); itsSpws[band].stateId->put (slicer1, vecint); vecint = band; itsSpws[band].dataDescId->put (slicer1, vecint); vecint = field; itsSpws[band].fieldId->put (slicer1, vecint); itsSpws[band].uvw->put (sliceru, myuvw); Matrix ones(itsNPol[band], nrbasel, 1); itsSpws[band].weight->put (slicer2, ones); itsSpws[band].sigma->put (slicer2, ones); // 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(shape3); Matrix weightsigma(shape3[0], 1, 1.); Array weightSpectrum; if (itsWriteWeightSpectrum) { weightSpectrum.resize (shape3); weightSpectrum = 1; } Double time = itsStartTime + itsNrTimes*itsStepTime + itsStepTime/2; Matrix myuvw(3, 1, 0); Vector vecint(1); Vector vectime(1, time); Vector vecinterval(1, itsStepTime); Vector vecfalse(1, False); // Define the slicers to put the data arrays. for (int j=0; jput (slicer3, defData); itsSpws[band].flag->put (slicer3, defFlags); if (itsWriteWeightSpectrum) { itsSpws[band].weightSpectrum->put (slicer3, Array(shape3, 1)); } itsSpws[band].flagRow->put (slicer1, vecfalse); itsSpws[band].time->put (slicer1, vectime); itsSpws[band].timeCentroid->put (slicer1, vectime); itsSpws[band].interval->put (slicer1, vecinterval); itsSpws[band].exposure->put (slicer1, vecinterval); vecint[0] = j; itsSpws[band].antenna1->put (slicer1, vecint); vecint[0] = i; itsSpws[band].antenna2->put (slicer1, vecint); vecint[0] = 0; itsSpws[band].feed1->put (slicer1, vecint); itsSpws[band].feed2->put (slicer1, vecint); itsSpws[band].processorId->put (slicer1, vecint); itsSpws[band].scanNumber->put (slicer1, vecint); itsSpws[band].arrayId->put (slicer1, vecint); itsSpws[band].observationId->put (slicer1, vecint); itsSpws[band].stateId->put (slicer1, vecint); vecint[0] = band; itsSpws[band].dataDescId->put (slicer1, vecint); vecint[0] = field; itsSpws[band].fieldId->put (slicer1, vecint); itsSpws[band].uvw->put (sliceru, myuvw); itsSpws[band].weight->put (slicer2, weightsigma); itsSpws[band].sigma->put (slicer2, weightsigma); 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; } // Define the global variables shared between the main functions. vector myRa; vector myDec; Matrix myAntPos; bool myWriteAutoCorr; bool myCalcUVW; bool myWriteWeightSpectrum; bool myCreateImagerColumns; bool myWriteRowWise; bool myDoSinglePart; int myNPart; int myNBand; Vector myNPol; Vector myNChan; int myNTime; int myNTimeField; int myTileSizeFreq; int myTileSize; //# in KBytes 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 KBytes bool myWriteHDF5; IPosition formTileShape (int tileSize, int tileNFreq, 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 tsf = tileNFreq; int ts = tileSize; if (tsf <= 0) { tsf = max(nfreq); // default is all channels } int tsp = max(npol); if (ts <= 0) { ts = 1024*1024; // default is 1 MByte } int tsr = std::max (1, ts / (tsp*tsf*8)); return IPosition(3,tsp,tsf,tsr); } void showHelp() { cout << "The program creates one or more MeasurementSets with a given number of" <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", "1e9", "Start frequency (Hz) per spw (1 value counts on for other spws)", "double"); params.create ("chanwidth", "1e6", "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 ("nspw", "1", "Number of spectral windows", "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 ("weightspectrum", "false", "Write WEIGHT_SPECTRUM column?", "bool"); params.create ("imagercolumns", "false", "Write imager columns (MODEL_DATA, CORRECTED_DATA)?", "bool"); params.create ("rowwise", "true", "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 (KBytes); default is 1024", "int"); params.create ("tilesizefreq", "-1", "Number of channels in data tiles; default is all channels", "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 (KBytes); default is tilesize", "int"); params.create ("ashdf5", "false", "Write the data in HDF5 format", "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 (params.getDoubleArray ("chanwidth")); myStartFreq = Vector (params.getDoubleArray ("startfreq")); 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 (params.getIntArray ("nchan")); myNPol = Vector (params.getIntArray ("npol")); myNTime = params.getInt ("ntime"); myNTimeField = params.getInt ("ntimefield"); myNPart = params.getInt ("nms"); myDoSinglePart = (myNPart == 0); if (myDoSinglePart) { myNPart = 1; } // Determine possible tile size. Default is no tiling. myTileSizeFreq = params.getInt ("tilesizefreq"); myTileSize = params.getInt ("tilesize") * 1024; // Determine nr of bands per part (i.e., ms). AlwaysAssertExit (myNPart > 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; } AlwaysAssertExit (myNPol.size() == 1 || myNPol.size() == uInt(myNBand)); if (myNPol.size() != uInt(myNBand)) { int np = myNPol[0]; myNPol.resize (myNBand, True); myNPol = np; } AlwaysAssertExit (myNChan.size() == 1 || myNChan.size() == uInt(myNBand)); if (myNChan.size() != uInt(myNBand)) { int nf = myNChan[0]; myNChan.resize (myNBand); myNChan = nf; } // Determine start and step frequency per band. AlwaysAssertExit (myStepFreq.size() == 1 || myStepFreq.size() == uInt(myNBand)); if (myStepFreq.size() != uInt(myNBand)) { double f = myStepFreq[0]; myStepFreq.resize (myNBand, True); myStepFreq = f; } AlwaysAssertExit (myStartFreq.size() == 1 || myStartFreq.size() == uInt(myNBand)); if (myStartFreq.size() != uInt(myNBand)) { myStartFreq.resize (myNBand, True); for (int i=1; i 0); AlwaysAssertExit (myStartFreq[i] > 0); } AlwaysAssertExit (myStepTime > 0); // Get remaining parameters. myWriteAutoCorr = params.getBool ("autocorr"); 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") * 1024; myWriteHDF5 = params.getBool ("ashdf5"); 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 { Vector rows(nant); indgen (rows); tab = tab(rows); } AlwaysAssert (nant>0, AipsError); ROArrayColumn posCol(tab, "POSITION"); posCol.getColumn (myAntPos); } return true; } void showParms() { cout << "writems parameters:" << endl; cout << " nms = " << myNPart << " " << myMsName << endl; int nant = myAntPos.ncolumn(); cout << " nant = " << nant << " (nbaseline = "; if (myWriteAutoCorr) { cout << nant*(nant+1) / 2; } else { cout << nant*(nant-1) / 2; } cout << ')' << 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, myTileSizeFreq, myNPol, myNChan); int tileSize = tileShape.product() * 8 / 1024; cout << " tilesize = " << tileSize << " KBytes" << endl; if (!myWriteHDF5 && myUseMultiFile) { cout << " multi" << (myUseMultiFile==1 ? "file" : "hdf5"); int multiBlockSize = myMultiBlockSize; if (multiBlockSize <= 0) { multiBlockSize = tileSize; } cout << " (blocksize = " << multiBlockSize << " KBytes)" << 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; CountedPtr msmaker; if (myWriteHDF5) { msmaker = new MSCreateHDF5(); } else { msmaker = new MSCreateCasa(); } IPosition dataTileShape = formTileShape (myTileSize, myTileSizeFreq, myNPol, myNChan); myTileSize = dataTileShape.product() * 8; if (myMultiBlockSize < 0) { myMultiBlockSize = myTileSize; } msmaker->init (myRa, myDec, myAntPos, myWriteAutoCorr, myCalcUVW, myWriteWeightSpectrum, myCreateImagerColumns, myNPol, myNChan, myStartFreq, myStepFreq, seqnr*nbpp, nbpp, 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) { msmaker->showCacheStatistics(); } return name; } void doAll() { 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-2.4.1/msfits/000077500000000000000000000000001321422335000145765ustar00rootroot00000000000000casacore-2.4.1/msfits/CMakeLists.txt000066400000000000000000000027231321422335000173420ustar00rootroot00000000000000# # 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 ) 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 MSFits.h DESTINATION include/casacore/msfits ) add_subdirectory (MSFits/test ${EXCL_ALL}) casacore-2.4.1/msfits/MSFits.h000066400000000000000000000044561321422335000161250ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/msfits/MSFits/000077500000000000000000000000001321422335000157435ustar00rootroot00000000000000casacore-2.4.1/msfits/MSFits/FitsIDItoMS.cc000066400000000000000000003211421321422335000203130ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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 Double FITSIDItoMS1::rdate = 0.; // initialize the class variable rdate String FITSIDItoMS1::array_p = ""; // initialize the class variable array_p SimpleOrderedMap FITSIDItoMS1::antIdFromNo(-1); // initialize the class variable antIdFromNo SimpleOrderedMap FITSIDItoMS1::digiLevels(0); // initialize the class variable digiLevels Vector FITSIDItoMS1::effChBw; // // Constructor // FITSIDItoMS1::FITSIDItoMS1(FitsInput& fitsin, const String& correlat, const Int& obsType, const Bool& initFirstMain) : BinaryTableExtension(fitsin), itsNrMSKs(10), itsMSKC(itsNrMSKs," "), itsMSKN(itsNrMSKs," "), itsMSKV(itsNrMSKs," "), itsgotMSK(itsNrMSKs,False), ///infile_p(fitsin), itsObsType(obsType), itsCorrelat(correlat), 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; weather_hasWater_p = False; weather_hasElectron_p = False; antIdFromNo.clear(); digiLevels.clear(); rdate = 0.; } // // 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(); // // 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 = ""; nStokes_p = 1; nBand_p = 1; 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; } } } 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; } } // 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) { 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)); } // 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((C::pi * r) / 2); } // Fred Schwab's rational approximation for 2-bit sampling with n = // 3.336 and optimally chosen v_0. See VLBA Correlat 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.isDefined(ant1)){ 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.isDefined(ant2)){ 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; } //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 = 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") { const double A = 5.36; const Double H = 0.87890625; Double bfacta, bfactc; Double Rm, gamma, alfa; 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 / C::pi; gamma = 1.0 * 64.0 / 63.0; rho = rho_2; } else { Rm = 5.8784; alfa = 0.882518; gamma = 3.335875 * 64.0 / 63.0; } bfactc = (gamma*gamma) / (A * Rm * alfa * H); bfacta = (gamma*gamma) / (A * Rm * H); if (ant1 != ant2) { // Cross-correlations vis *= Complex(bfactc); } else { // Auto-correlations for (Int p=0; p=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 tmp(nCorr); tmp=1.0; msc.sigma().put(putrow,tmp); tmp=0.0; msc.weight().put(putrow,tmp); 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; // Free FFTPack work buffer work.putStorage(workptr, worksave); } // 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()); ROScalarColumn name(anTab,"ANNAME"); ROArrayColumn antXYZ(anTab,"STABXYZ"); ROArrayColumn dantXYZ(anTab,"DERXYZ"); // following is for space-born telescope //ROScalarColumn orbp(anTab,"ORBPARM"); ROScalarColumn anNo(anTab,"NOSTA"); ROScalarColumn mntid(anTab,"MNTSTA"); ROArrayColumn offset(anTab,"STAXOF"); ROScalarColumn 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.ndefined() - 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: // ROArrayColumn 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(""); ROScalarColumn time(anTab, "TIME"); ROScalarColumn timeint; ROScalarColumn timeintd; try{ timeint.attach(anTab, "TIME_INTERVAL"); } catch(AipsError){ timeintd.attach(anTab, "TIME_INTERVAL"); *itsLog << LogIO::NORMAL << "Note: this ANTENNA table uses double precision for TIME_INTERVAL. Convention is single." << LogIO::POST; } ROScalarColumn name(anTab, "ANNAME"); ROScalarColumn anNo(anTab, "ANTENNA_NO"); ROScalarColumn array(anTab, "ARRAY"); ROScalarColumn fqid(anTab, "FREQID"); ROScalarColumn digLev(anTab, "NO_LEVELS"); ROScalarColumn poltya(anTab, "POLTYA"); ROScalarColumn poltyb(anTab, "POLTYB"); // if the values for all bands are the same, POLAA, POLAB, POLCALA and POLCALB can be scalar Bool POLAisScalar = False; ROArrayColumn polaa; ROArrayColumn polab; ROScalarColumn polaaS; ROScalarColumn polabS; try{ polaa.attach(anTab, "POLAA"); polab.attach(anTab, "POLAB"); } catch(AipsError x){ polaaS.attach(anTab, "POLAA"); polabS.attach(anTab, "POLAB"); POLAisScalar = True; *itsLog << LogIO::WARN << "Treating POLAA and POLAB columns in input ANTENNA table as scalar," << endl << " i.e. using same value for all bands." << LogIO::POST; } ROArrayColumn polcala; ROArrayColumn 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; } } // ROArrayColumn 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(); ROScalarColumn 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; effChBw.resize(nSpW); // The type of the column changes according to the number of entries if (nIF_p==1) { ROScalarColumn colIFFreq(fqTab, "BANDFREQ"); ROScalarColumn colChWidth(fqTab, "CH_WIDTH"); ROScalarColumn colTotalBW(fqTab,"TOTAL_BANDWIDTH"); ROScalarColumn colSideBand(fqTab, "SIDEBAND"); for (Int i=0; i colIFFreq(fqTab, "BANDFREQ"); ROArrayColumn colChWidth(fqTab, "CH_WIDTH"); ROArrayColumn colTotalBW(fqTab, "TOTAL_BANDWIDTH"); ROArrayColumn 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 ROScalarColumn id; if(suTab.tableDesc().isColumn("SOURCE_ID")){ id.attach(suTab, "SOURCE_ID"); } else if(suTab.tableDesc().isColumn("ID_NO.")){ *itsLog << LogIO::WARN << "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.")); } ROScalarColumn name(suTab,"SOURCE"); ROScalarColumn qual(suTab,"QUAL"); ROScalarColumn code(suTab,"CALCODE"); ROScalarColumn fqid(suTab,"FREQID"); // if the values are the same for all bands, the flux, alpha, freqoff, sysvel, and restfreq columns can be scalar ROArrayColumn iflux; ROArrayColumn qflux; ROArrayColumn uflux; ROArrayColumn vflux; ROArrayColumn alpha; ROArrayColumn foffset; ROArrayColumn foffsetD; ROArrayColumn sysvel; ROArrayColumn restfreq; ROScalarColumn ifluxS; ROScalarColumn qfluxS; ROScalarColumn ufluxS; ROScalarColumn vfluxS; ROScalarColumn alphaS; ROScalarColumn foffsetS; ROScalarColumn sysvelS; ROScalarColumn 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(AipsError x){ foffsetD.attach(suTab,"FREQOFF"); // fq. offset *itsLog << LogIO::WARN << "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(AipsError 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 foffsetS.attach(suTab,"FREQOFF"); // fq. offset sysvelS.attach(suTab,"SYSVEL"); // sys vel. (m/s) restfreqS.attach(suTab,"RESTFREQ"); // rest freq. (hz) *itsLog << LogIO::WARN << "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; } ROScalarColumn ra(suTab,"RAEPO"); //degrees ROScalarColumn dec(suTab,"DECEPO"); //degrees ROScalarColumn equinox; ROScalarColumn 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"); } ROScalarColumn raapp(suTab,"RAAPP"); //degrees ROScalarColumn decapp(suTab,"DECAPP"); //degrees ROScalarColumn veltype(suTab,"VELTYP"); // ROScalarColumn veldef(suTab,"VELDEF"); // ROScalarColumn pmra(suTab,"PMRA"); //deg/day ROScalarColumn pmdec(suTab,"PMDEC"); //deg/day ROScalarColumn 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; return False; } 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(""); ROScalarColumn time(tyTab, "TIME"); ROScalarColumn timeint(tyTab, "TIME_INTERVAL"); ROScalarColumn anNo(tyTab, "ANTENNA_NO"); ROArrayColumn tsys_1; ROArrayColumn tsys_2; ROScalarColumn tsys_1S; ROScalarColumn 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(AipsError){ 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; *itsLog << LogIO::WARN << "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()); *itsLog << LogIO::WARN << "not yet implemented" << LogIO::POST; return False; } Bool FITSIDItoMS1::fillWeatherTable() { *itsLog << LogOrigin("FitsIDItoMS()", "fillWeatherTable"); MSWeatherColumns& msWeather(msc_p->weather()); Int nVal=nrows(); Table wxTab = oldfullTable(""); ROScalarColumn time(wxTab, "TIME"); ROScalarColumn timeint(wxTab, "TIME_INTERVAL"); ROScalarColumn anNo(wxTab, "ANTENNA_NO"); ROScalarColumn temperature(wxTab, "TEMPERATURE"); ROScalarColumn pressure(wxTab, "PRESSURE"); ROScalarColumn dewpoint(wxTab, "DEWPOINT"); ROScalarColumn wind_velocity(wxTab, "WIND_VELOCITY"); ROScalarColumn wind_direction(wxTab, "WIND_DIRECTION"); ROScalarColumn wvr_h2o; if(weather_hasWater_p) wvr_h2o.attach(wxTab, "WVR_H2O"); ROScalarColumn ionos_electron; if(weather_hasElectron_p) wvr_h2o.attach(wxTab, "IONOS_ELECTRON"); Int outRow=-1; for (Int inRow=0; inRowsetEpochRef(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; if (firstSyscal && extname == "SYSTEM_TEMPERATURE") { addSyscal=True; firstSyscal=False; } if (firstWeather && extname == "WEATHER") { addWeather=True; firstWeather=False; } setupMeasurementSet(msFile, useTSM, mainTbl, addCorrMode, addSyscal, addWeather); 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 =="BASELINE" || extname =="BANDPASS" || extname =="CALIBRATION" ){ *itsLog << LogIO::WARN << "FITS-IDI table " << extname << " not yet supported. Will ignore it." << LogIO::POST; return False; } else { *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-2.4.1/msfits/MSFits/FitsIDItoMS.h000066400000000000000000000242761321422335000201650ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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); ~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 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); // 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; 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; MeasurementSet ms_p; MSColumns* msc_p; static Bool firstMain; static Bool firstSyscal; static Bool firstWeather; 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; static SimpleOrderedMap antIdFromNo; static SimpleOrderedMap digiLevels; static Vector effChBw; // //# 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-2.4.1/msfits/MSFits/MSFitsIDI.cc000066400000000000000000000356221321422335000177550ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: //---------------------------------------------------------------------------- #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){ Table::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; } // 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; // Loop over all HDU in the FITS-IDI file Bool initFirstMain = True; while (infits.err() == FitsIO::OK && !infits.eof()) { // Fetch correlator name 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; } } // 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); 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.datasize() > 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)=="WEATHER") { Table mssub(itsMSOut+"_tmp/"+subTableName(isub)+"/WEATHER",Table::Update); mssub.rename (itsMSOut+"/WEATHER",Table::New); msmain.rwKeywordSet().defineTable("WEATHER",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-2.4.1/msfits/MSFits/MSFitsIDI.h000066400000000000000000000070731321422335000176160ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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-2.4.1/msfits/MSFits/MSFitsInput.cc000066400000000000000000004246031321422335000204500ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: MSFitsInput.cc 21580 2015-03-24 08:37:00Z gervandiepen $ // #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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), _rotateAnts(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.getMesg() << LogIO::EXCEPTION; } } else { //else fill row wise try { fillMSMainTable(nField, nSpW); } catch(const AipsError& ex) { _log << LogOrigin("MSFitsInput", __func__) << ex.getMesg() << LogIO::EXCEPTION; } } // now handle the BinaryTable extensions for the subtables Bool haveAn = False, haveField = False, haveSpW = 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 { _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("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.getMesg() << 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(); Table::deleteTable(name, True); } ThrowCc(ex.getMesg()); } } 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", "checkInput") << "file does not start with standard hdu record." << LogIO::EXCEPTION; } _log << LogOrigin("MSFitsInput", "checkInput") << 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", "checkInput") << "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", "checkInput") << "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 time Int iTime0 = getIndex(pType, "DATE", 0); Int iTime1 = getIndex(pType, "DATE", 1); // get index for source Int iSource = getIndex(pType, "SOURCE"); // get index for Freq Int iFreq = getIndex(pType, "FREQSEL"); // 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 = Int(_priGroup.parm(iFreq)); // Extract field Id Int fieldId = 0; if (iSource >= 0) { // make 0-based fieldId = (Int) _priGroup.parm(iSource) - 1; } Float baseline = _priGroup.parm(iBsln); Int arrayId = Int(100.0 * (baseline - Int(baseline) + 0.001)); _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 std::pair ants = _extractAntennas(baseline); 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 time Int iTime0 = getIndex(pType, "DATE", 0); Int iTime1 = getIndex(pType, "DATE", 1); // get index for source Int iSource = getIndex(pType, "SOURCE"); // get index for Freq Int iFreq = getIndex(pType, "FREQSEL"); // 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 = Int(_priGroup.parm(iFreq)); // 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 Float baseline = _priGroup.parm(iBsln); Int arrayId = Int(100.0 * (baseline - Int(baseline) + 0.001)); _nArray = max(_nArray, arrayId + 1); std::pair ants = _extractAntennas(baseline); 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::fillAntennaTable(BinaryTable& bt) { static const Regex trailing(" *$"); // trailing blanks TableRecord btKeywords = bt.getKeywords(); Int nAnt = bt.nrows(); Table anTab = bt.fullTable(); ROScalarColumn 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 String arrnam = "Unknown"; if (btKeywords.isDefined("ARRNAM")) { arrnam = btKeywords.asString("ARRNAM"); } // 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()); ROScalarColumn name(anTab, "ANNAME"); ROScalarColumn mountType(anTab, "MNTSTA"); ROScalarColumn offset(anTab, "STAXOF"); ROScalarColumn polangleA(anTab, "POLAA"); ROScalarColumn polangleB(anTab, "POLAB"); ROArrayColumn 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 = ROScalarColumn(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); if (_rotateAnts) { if (_array == "VLA") { // 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); if (allNearAbs(arrayXYZ, vlaCenter.getValue().getValue(), 10)) { 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; } else { _log << LogOrigin("MSFitsInput", __func__) << 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; } } else { _log << LogOrigin("MSFitsInput", __func__) << LogIO::WARN << "Array " + _array + " is not a candidate to have " << "its antenna positions rotated. 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: case 5: case 6: 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: // ROArrayColumn 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(); ROScalarColumn 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) { ROScalarColumn colIFFreq(fqTab,"IF FREQ"); ROScalarColumn colChWidth(fqTab,"CH WIDTH"); ROScalarColumn colTotalBandwidth(fqTab,"TOTAL BANDWIDTH"); for (Int i=0; i colIFFreq(fqTab,"IF FREQ"); ROArrayColumn colChWidth(fqTab,"CH WIDTH"); ROArrayColumn colTotalBandwidth(fqTab,"TOTAL BANDWIDTH"); try{ colIFFreq.getColumn(ifFreq); colChWidth.getColumn(chWidth); colTotalBandwidth.getColumn(totalBandwidth); }catch(AipsError x) { _log << LogOrigin("MSFitsInput", "fillSpectralWindowTable") << LogIO::DEBUG1 << x.getMesg() << 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(); ROScalarColumn id(suTab, "ID. NO."); ROScalarColumn name(suTab, "SOURCE"); ROScalarColumn qual(suTab, "QUAL"); Bool multiqual = False; Int minqual, maxqual; minMax(minqual, maxqual, qual.getColumn()); if (minqual != maxqual) multiqual = True; ROScalarColumn code(suTab, "CALCODE"); // ROScalarColumn iflux(suTab,"IFLUX"); // etc Q, U, V (Jy) ROScalarColumn ra(suTab, "RAEPO"); //degrees ROScalarColumn dec(suTab, "DECEPO"); //degrees ROScalarColumn raapp(suTab, "RAAPP"); //degrees ROScalarColumn decapp(suTab, "DECAPP"); //degrees ROScalarColumn epoch(suTab, "EPOCH"); //years ROScalarColumn pmra(suTab, "PMRA"); //deg/day ROScalarColumn 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 { ROArrayColumn restfreq(suTab,"RESTFREQ"); // Hz ROArrayColumn 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 AipsError& x) { ThrowIf(throwImmediately, x.getMesg()); 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()); ROScalarColumn restfreq(suTab,"RESTFREQ"); // Hz ROScalarColumn 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(); } SimpleOrderedMap , Int> sourceFieldIndex(-1); // 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.isDefined(myfldspw)){ sourceFieldIndex.define(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(); ROScalarColumn 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) { ROScalarColumn colIFFreq(fqTab, "IF FREQ"); ROScalarColumn colChWidth(fqTab, "CH WIDTH"); ROScalarColumn 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 { ROArrayColumn colIFFreq(fqTab, "IF FREQ"); ROArrayColumn colChWidth(fqTab, "CH WIDTH"); ROArrayColumn 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 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 { ROScalarColumn colDate(tb, TType(iTime0)); ROScalarColumn colUU(tb, TType(iU)); ROScalarColumn colVV(tb, TType(iV)); ROScalarColumn colWW(tb, TType(iW)); ROScalarColumn colBL(tb, TType(iBsln)); ROArrayColumn colVis(tb, TType(iVis)); 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) { ROScalarColumn colFrqSel(tb, TType(iFreq)); freqId = (Int) colFrqSel.asfloat(0); } // Extract field Id Int fieldId = 0; if (iSource >= 0) { ROScalarColumn 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 Float baseline = colBL.asfloat(0); Int arrayId = Int(100.0 * (baseline - Int(baseline) + 0.001)); _nArray = max(_nArray, arrayId + 1); std::pair ants = _extractAntennas(baseline); 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 AipsError& x) { _log << LogOrigin("MSFitsInput", "fillMSMainTable") << "Exception while filling MS main table. " << x.getMesg() << 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(Float baseline) { Int ant1 = Int(baseline) / 256; Int ant2 = Int(baseline) - ant1 * 256; _nAntRow = max(_nAntRow, ant1); _nAntRow = max(_nAntRow, ant2); _uniqueAnts.insert(ant1); _uniqueAnts.insert(ant2); // make 0-based ant1--; ant2--; return make_pair(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(); ROScalarColumn id(suTab, "ID. NO."); ROScalarColumn name(suTab, "SOURCE"); ROScalarColumn qual(suTab, "QUAL"); Bool multiqual = False; Int minqual, maxqual; minMax(minqual, maxqual, qual.getColumn()); if (minqual != maxqual) multiqual = True; ROScalarColumn code(suTab, "CALCODE"); // ROScalarColumn iflux(suTab,"IFLUX"); // etc Q, U, V (Jy) ROScalarColumn ra(suTab, "RAEPO"); //degrees ROScalarColumn dec(suTab, "DECEPO"); //degrees ROScalarColumn raapp(suTab, "RAAPP"); //degrees ROScalarColumn decapp(suTab, "DECAPP"); //degrees ROScalarColumn epoch(suTab, "EPOCH"); //years ROScalarColumn pmra(suTab, "PMRA"); //deg/day ROScalarColumn 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{ ROArrayColumn restfreq(suTab,"RESTFREQ"); // Hz ROArrayColumn 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; } ROScalarColumn restfreq(suTab,"RESTFREQ"); // Hz ROScalarColumn 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-2.4.1/msfits/MSFits/MSFitsInput.h000066400000000000000000000327441321422335000203130ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: MSFitsInput.h 21531 2014-12-24 11:46:02Z gervandiepen $ #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; // // 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: // 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); // The destructor is fairly trivial. ~MSFitsInput(); // 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); // A simultaneous change to MSFitsOutput means that no longer are // antenna positions being rotated when written to UVFITS. Calling // this method with b=True will perform the reverse of a rotation // when converting from uvfits to MS for relevant UVFITS files which // were written prior to this change. Else no rotation of antenna // positions is done. void rotateAntennaPositions(Bool b) { _rotateAnts = b; } protected: // 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); private: //# The default constructor is private and undefined MSFitsInput(); //# The copy constructor is private and undefined MSFitsInput(const MSFitsInput& other); //# The assignment operator is private and undefined MSFitsInput& operator=(const MSFitsInput& other); 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, _rotateAnts; std::pair _extractAntennas(Float baseline); }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/msfits/MSFits/MSFitsOutput.cc000066400000000000000000003235031321422335000206460ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: MSFitsOutput.cc 21521 2014-12-10 08:06:42Z gervandiepen $ #include #include #include #include #include #include #include #include #include #include #include #include #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 { 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 { ROMSObservationColumns obsCols(_ms.observation()); if ( obsCols.nrow() > 0 && (obsCols.telescopeName()(0) == "WSRT" || obsCols.telescopeName()(0) == "LOFAR") ) { 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; FitsOutput* fitsOutput = _writeMain( refPixelFreq, refFreq, chanbw, outfile, spwidMap, nrspw, fieldidMap, doMultiSource ); Bool ok = fitsOutput; ThrowIf ( ! ok, "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; } } // flush output to disk delete fitsOutput; } 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 ) { 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; } FitsOutput *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; FitsOutput *outfile = 0; 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()); ROMSFieldColumns 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 / C::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 AipsError& x) { os << LogOrigin("MSFitsOutput", __func__) << LogIO::WARN << "No source table in MS. " << x.getMesg() << 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) { 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); } } // 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) { ROMSMainColumns 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 desc.addField("baseline", 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 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(_ms, MS::columnName(MS::TIME)); ek.define("date-obs", toFITSDate(intm(0) / C::day)); // First time entry } // TELESCOP INSTRUME ROMSObservationColumns 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 = 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(writer.row(), "baseline"); 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; } } Vector antnumbers; handleAntNumbers(_ms, antnumbers); // 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 *obaseline = antnumbers(inant1(tbfrownr)) * 256 + antnumbers(inant2( tbfrownr)) + inarray(tbfrownr) * 0.01; // 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(FitsOutput *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, 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(FitsOutput *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 = ROMSColumns(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(); ROMSAntennaColumns 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(); ROMSAntennaColumns 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(); ROMSFeedColumns 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, 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")) { *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 = 7; } else if (mount.contains("BIZARRE")) { *mntsta = 4; // 5, 6 } else { *mntsta = 7; // fits does not use anyway, put it 7 } *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 { _checkReceptorAngles(ra, antToRA[antnum], antnum); } } } 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& 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()); ROMSFieldColumns 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()); // ROMSSourceColumns 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; ROMSSourceColumns* sourceColumns = 0; ColumnsIndex* srcInx = 0; RecordFieldPtr* srcInxFld = 0; if (!ms.source().isNull()) { sourceTable = new MSSource(ms.source()); sourceColumns = new ROMSSourceColumns(*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, 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 (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); } *calcode = sourceColumns->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(FitsOutput *output, const MeasurementSet &ms, const Table& syscal, const Block& spwidMap, uInt nrif, Bool combineSpw) { LogIO os(LogOrigin("MSFitsOutput", "writeTY")); const MSSysCal subtable(syscal); ROMSSysCalColumns 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) ROMSColumns 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 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(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("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 = ROMSFeedColumns(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; { ROMSColumns 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); ROMSSysCalColumns 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, 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); ROMSSysCalColumns 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(FitsOutput *output, const MeasurementSet &ms) { LogIO os(LogOrigin("MSFitsOutput", "writeWX")); const MSWeather subtable(ms.weather()); ROMSWeatherColumns 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) ROMSColumns mscol(ms); refTime = floor(mscol.time()(0) / C::day) * C::day; //} //MEpoch measTime = ROMSColumns(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, 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 from Pa to mbar *pressure = weatherColumns.pressure()(i) / 100; } 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; } void MSFitsOutput::getStartHA(Double& startTime, Double& startHA, const MeasurementSet& ms, uInt rownr) { ROMSColumns 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. { ROMSSysCalColumns 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 ROMSObservationColumns obscol(ms.observation()); String arrayName; if (obscol.nrow() > 0) arrayName = obscol.telescopeName()(0); ROMSAntennaColumns 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-2.4.1/msfits/MSFits/MSFitsOutput.h000066400000000000000000000253521321422335000205110ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: MSFitsOutput.h 21521 2014-12-10 08:06:42Z gervandiepen $ #ifndef MS_MSFITSOUTPUT_H #define MS_MSFITSOUTPUT_H #include #include #include #include namespace casacore { //# Forward Declarations class FitsOutput; template class ScalarColumn; class Table; template class Block; template class Vector; // // 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 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 rawms // @param column data column to write // @param spwidMap spwidMap[inp_spw] = output_spw, if inp_spw is selected // -1 otherwise. // @param nrspw # of selected spws. // @param startchan First channel // @param nchan # of channels // @param stepchan channel stride // @param fieldidMap fieldidMap[inp_fld] = output_fld, if inp_fld is selected // -1 otherwise. // @param asMultiSource If true, write a multisource UVFITS file. // @param combineSpw If true, export the spectral window(s) as IF(s). // @param padWithFlags If true && combineSpw==true, pad the spws with // flags as necessary to fit the IF structure. // @param avgchan average every N channels FitsOutput* _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(FitsOutput *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( 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 ); // Write the WX table. static Bool writeWX(FitsOutput *output, const MeasurementSet& ms); // 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-2.4.1/msfits/MSFits/MSFitsOutputAstron.cc000066400000000000000000002115521321422335000220350ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include #include #include #include #include #include #include #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 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")); 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; { ROScalarColumn 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; { ROScalarColumn 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) { ROScalarColumn inarrayname(obsTable, MSObservation::columnName (MSObservation::TELESCOPE_NAME)); doWsrt = inarrayname(0) == "WSRT"; } } MSField fieldTable(rawms.field()); ROMSFieldColumns msfc(fieldTable); Vector radec = msfc.phaseDirMeas(0).getAngle().getValue(); radec *=180.0/C::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; } ROScalarColumn spwId(ddTable, MSDataDescription::columnName(MSDataDescription::SPECTRAL_WINDOW_ID)); ROScalarColumn polId(ddTable, MSDataDescription::columnName(MSDataDescription::POLARIZATION_ID)); ROScalarColumn numcorr(polTable, MSPolarization::columnName(MSPolarization::NUM_CORR)); ROScalarColumn numchan(spectralTable, MSSpectralWindow::columnName(MSSpectralWindow::NUM_CHAN)); ROArrayColumn frequencies(spectralTable, MSSpectralWindow::columnName(MSSpectralWindow::CHAN_FREQ)); ROArrayColumn stokesTypes(polTable, MSPolarization::columnName(MSPolarization::CORR_TYPE)); ROScalarColumn totalbw(spectralTable, MSSpectralWindow::columnName(MSSpectralWindow::TOTAL_BANDWIDTH)); ROScalarColumn 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; ROTableColumn 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 = ROScalarColumn(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"; { ROTableColumn 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 { ROScalarColumn 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 ROMSObservationColumns 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. ROArrayColumn indata(sortTable, columnName); ROArrayColumn inweightscalar(sortTable, MS::columnName(MS::WEIGHT)); ROArrayColumn inweightarray; if (hasWeightArray) { inweightarray.attach(sortTable, MS::columnName(MS::WEIGHT_SPECTRUM)); } ROScalarColumn inrowflag(sortTable, MS::columnName(MS::FLAG_ROW)); ROArrayColumn indataflag(sortTable, MS::columnName(MS::FLAG)); ROArrayColumn inuvw(sortTable, MS::columnName(MS::UVW)); ROScalarColumn intime(sortTable, MS::columnName(MS::TIME)); ROScalarColumn inant1(sortTable, MS::columnName(MS::ANTENNA1)); ROScalarColumn inant2(sortTable, MS::columnName(MS::ANTENNA2)); ROScalarColumn inarray(sortTable, MS::columnName(MS::ARRAY_ID)); ROScalarColumn inspwinid(sortTable, MS::columnName(MS::DATA_DESC_ID)); ROScalarColumn infieldid; ROScalarColumn 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()); ROArrayColumn inchanfreq (specTable, MSSpectralWindow::columnName(MSSpectralWindow::CHAN_FREQ)); ROScalarColumn intotbw (specTable, MSSpectralWindow::columnName(MSSpectralWindow::TOTAL_BANDWIDTH)); ROScalarColumn insideband (specTable, MSSpectralWindow::columnName(MSSpectralWindow::NET_SIDEBAND)); Bool doWsrt = False; String telescopeName; { MSObservation obsTable(ms.observation()); if (obsTable.nrow() > 0) { ROScalarColumn 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()); ROScalarColumn 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 = ROMSColumns(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(); ROMSAntennaColumns antennaCols (antennaTable); // SELECT antennas for the current sub-array // MSAntenna antennaTable = ms.antenna() //(ms.antenna().col(MSAntenna::columnName(MSAntenna::ARRAY_ID)) == // Int(arraynum)); ROScalarColumn inantname(antennaCols.station()); ROScalarColumn antid(antennaCols.name()); ROScalarColumn inantmount(antennaCols.mount()); MPosition::ROScalarColumn inantposition(antennaCols.positionMeas()); ROArrayColumn inantoffset(antennaCols.offset()); const uInt nant = antennaTable.nrow(); os << LogIO::NORMAL << "Found " << nant << " antennas in array #" << arraynum+1 << LogIO::POST; MSFeed feedTable = ms.feed(); ROMSFeedColumns feedCols (feedTable); ROArrayColumn inpoltype(feedCols.polarizationType()); ROScalarColumn 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()); ROMSFieldColumns msfc(fieldTable); const ROScalarColumn& insrcid=msfc.sourceId(); const ROScalarColumn& inname=msfc.name(); // If source table exists, access it // This is for case where SOURCE guaranteed to exist: // MSSource sourceTable(ms.source()); // ROMSSourceColumns 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; ROMSSourceColumns* sourceColumns=0; ColumnsIndex* srcInx=0; RecordFieldPtr* srcInxFld=0; if (!ms.source().isNull()) { sourceTable = new MSSource(ms.source()); sourceColumns = new ROMSSourceColumns(*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; } ROScalarColumn 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")) { ROScalarColumn velTypeCol (spectralTable, "NFRA_VELOCDEFINITION"); velType = velTypeCol(0); velType.upcase(); header.define("VELTYP", velType); if (spectralTable.tableDesc().isColumn ("NFRA_CONVERSIONTYPE")) { ROScalarColumn 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.type()==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.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 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); ROMSSysCalColumns 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) ROMSColumns 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 = ROMSFeedColumns(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; { ROMSColumns 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); ROMSSysCalColumns 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); ROMSSysCalColumns 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. ROScalarColumn ant1col(ms, MS::columnName(MS::ANTENNA1)); ROScalarColumn 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 namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class String; class FitsOutput; class MeasurementSet; class Table; template class Block; template class Vector; // // 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-2.4.1/msfits/MSFits/SDAntennaHandler.cc000066400000000000000000000430501321422335000213650ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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-2.4.1/msfits/MSFits/SDAntennaHandler.h000066400000000000000000000120071321422335000212250ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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; template class Vector; // // // 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-2.4.1/msfits/MSFits/SDDataDescHandler.cc000066400000000000000000000115631321422335000214550ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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-2.4.1/msfits/MSFits/SDDataDescHandler.h000066400000000000000000000070211321422335000213110ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #ifndef MS_SDDATADESCHANDLER_H #define MS_SDDATADESCHANDLER_H #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class ColumnsIndex; class MeasurementSet; class MSDataDescription; class MSDataDescColumns; class Record; template class Vector; // // // 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-2.4.1/msfits/MSFits/SDFITSHandler.cc000066400000000000000000000216141321422335000205500ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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-2.4.1/msfits/MSFits/SDFITSHandler.h000066400000000000000000000100031321422335000204000ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #ifndef MS_SDFITSHANDLER_H #define MS_SDFITSHANDLER_H #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class CopyRecordToTable; class MeasurementSet; class Record; class Table; template class Vector; // // // 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-2.4.1/msfits/MSFits/SDFeedHandler.cc000066400000000000000000000412711321422335000206470ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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) { SimpleOrderedMap polTypeMap(-1); for (uInt i=0;i #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class ColumnsIndex; class MeasurementSet; class MSFeed; class MSFeedColumns; class Record; template class Vector; // // // 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-2.4.1/msfits/MSFits/SDFieldHandler.cc000066400000000000000000000326371321422335000210350ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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-2.4.1/msfits/MSFits/SDFieldHandler.h000066400000000000000000000106301321422335000206640ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #ifndef MS_SDFIELDHANDLER_H #define MS_SDFIELDHANDLER_H #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class MeasurementSet; class MSField; class MSFieldColumns; class String; class Record; class ColumnsIndex; template class Vector; template class Matrix; // // // 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-2.4.1/msfits/MSFits/SDHistoryHandler.cc000066400000000000000000000110761321422335000214450ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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-2.4.1/msfits/MSFits/SDHistoryHandler.h000066400000000000000000000072101321422335000213020ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #ifndef MS_SDHISTORYHANDLER_H #define MS_SDHISTORYHANDLER_H #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class MeasurementSet; class MSHistory; class MSHistoryColumns; class Record; template class Vector; // // // 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-2.4.1/msfits/MSFits/SDMainHandler.cc000066400000000000000000000206001321422335000206610ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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-2.4.1/msfits/MSFits/SDMainHandler.h000066400000000000000000000074741321422335000205410ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #ifndef MS_SDMAINHANDLER_H #define MS_SDMAINHANDLER_H #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class MeasurementSet; class MSMainColumns; class MEpoch; class MVTime; class Record; template class Vector; template class Matrix; // // // 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-2.4.1/msfits/MSFits/SDObservationHandler.cc000066400000000000000000000263511321422335000223010ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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-2.4.1/msfits/MSFits/SDObservationHandler.h000066400000000000000000000107411321422335000221370ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #ifndef MS_SDOBSERVATIONHANDLER_H #define MS_SDOBSERVATIONHANDLER_H #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class ColumnsIndex; class MeasurementSet; class MSObservation; class MSObservationColumns; class Record; class String; template class Vector; // // // 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-2.4.1/msfits/MSFits/SDPointingHandler.cc000066400000000000000000000242541321422335000215750ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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-2.4.1/msfits/MSFits/SDPointingHandler.h000066400000000000000000000116631321422335000214370ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #ifndef MS_SDPOINTINGHANDLER_H #define MS_SDPOINTINGHANDLER_H #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class MeasurementSet; class MSPointing; class MSPointingColumns; class Record; template class Vector; // // // 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-2.4.1/msfits/MSFits/SDPolarizationHandler.cc000066400000000000000000000225541321422335000224620ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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 SimpleOrderedMap polTypeMap(-1); 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; default: // the two keys are identical to each other and to the stokes type key1 = key2 = stokesValue; break; } } } //# NAMESPACE CASACORE - END casacore-2.4.1/msfits/MSFits/SDPolarizationHandler.h000066400000000000000000000100711321422335000223130ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #ifndef MS_SDPOLARIZATIONHANDLER_H #define MS_SDPOLARIZATIONHANDLER_H #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class ColumnsIndex; class MeasurementSet; class MSPolarization; class MSPolarizationColumns; class Record; template class Vector; // // // 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-2.4.1/msfits/MSFits/SDSourceHandler.cc000066400000000000000000000374161321422335000212520ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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-2.4.1/msfits/MSFits/SDSourceHandler.h000066400000000000000000000106571321422335000211120ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #ifndef MS_SDSOURCEHANDLER_H #define MS_SDSOURCEHANDLER_H #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class ColumnsIndex; class MeasurementSet; class MSSource; class MSSourceColumns; class Record; template class Vector; // // // 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-2.4.1/msfits/MSFits/SDSpWinHandler.cc000066400000000000000000000407501321422335000210450ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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 uInt *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-2.4.1/msfits/MSFits/SDSpWinHandler.h000066400000000000000000000121401321422335000206770ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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-2.4.1/msfits/MSFits/SDSysCalHandler.cc000066400000000000000000000326561321422335000212110ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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-2.4.1/msfits/MSFits/SDSysCalHandler.h000066400000000000000000000101331321422335000210350ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #ifndef MS_SDSYSCALHANDLER_H #define MS_SDSYSCALHANDLER_H #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class MeasurementSet; class MSSysCal; class MSSysCalColumns; class Record; template class Vector; // // // 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-2.4.1/msfits/MSFits/SDWeatherHandler.cc000066400000000000000000000365601321422335000214100ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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-2.4.1/msfits/MSFits/SDWeatherHandler.h000066400000000000000000000077601321422335000212520ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #ifndef MS_SDWEATHERHANDLER_H #define MS_SDWEATHERHANDLER_H #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class MeasurementSet; class MSWeather; class MSWeatherColumns; class Record; template class Vector; // // // 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-2.4.1/msfits/MSFits/test/000077500000000000000000000000001321422335000167225ustar00rootroot00000000000000casacore-2.4.1/msfits/MSFits/test/CMakeLists.txt000066400000000000000000000004671321422335000214710ustar00rootroot00000000000000set (tests tfits2ms tMSConcat tMSFITSInput tMSFITSOutput tMSFitsSelection ) foreach (test ${tests}) add_executable (${test} ${test}) target_link_libraries (${test} casa_msfits) add_test (${test} ${CMAKE_SOURCE_DIR}/cmake/cmake_assay ./${test}) add_dependencies(check ${test}) endforeach (test) casacore-2.4.1/msfits/MSFits/test/tMSConcat.cc000066400000000000000000000072211321422335000210660ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { cerr << x.getMesg() << 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-2.4.1/msfits/MSFits/test/tMSConcat.run000066400000000000000000000023301321422335000213010ustar00rootroot00000000000000#!/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. # # $Id$ #----------------------------------------------------------------------------- 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-2.4.1/msfits/MSFits/test/tMSFITSInput.cc000066400000000000000000000122721321422335000214460ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: tMSConcat.cc 21563 2015-02-16 07:05:15Z gervandiepen $ #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 AipsError& x) { thrown = True; } AlwaysAssert(thrown, AipsError); { cout << "test rotating antenna positions" << endl; fitsfile = datadir + "regression/unittest/uvfits/casa_4.6_vla.uvfits"; String msfile = "rotated_positions.ms"; removeIfNecessary(msfile); SHARED_PTR reader(new MSFitsInput(msfile, fitsfile)); reader->rotateAntennaPositions(True); reader->readFitsFile(); MeasurementSet ms(msfile); SHARED_PTR md(new MSMetaData(&ms, 50)); vector pos = md->getAntennaPositions(vector(1, 0)); Vector expec(3); expec[0] = -1601145.65187839; expec[1] = -5041988.31653595; expec[2] = 3554878.93106461; AlwaysAssert(allNearAbs(pos[0].getValue().getValue(), expec, 1e-6), AipsError); msfile = "nonrotated_positions.ms"; removeIfNecessary(msfile); reader.reset(new MSFitsInput(msfile, fitsfile)); reader->readFitsFile(); ms = MeasurementSet(msfile); md.reset(new MSMetaData(&ms, 50)); pos = md->getAntennaPositions(vector(1, 0)); AlwaysAssert(! allNearAbs(pos[0].getValue().getValue(), expec, 1e-6), AipsError); fitsfile = datadir + "regression/unittest/uvfits/casa_4.7_vla.uvfits"; msfile = "4_7.ms"; removeIfNecessary(msfile); reader.reset(new MSFitsInput(msfile, fitsfile)); // This should have no effect on processing now, since in this uvfits // file, the array position is at the ITRF origin reader->rotateAntennaPositions(True); reader->readFitsFile(); ms = MeasurementSet(msfile); md.reset(new MSMetaData(&ms, 50)); pos = md->getAntennaPositions(vector(1, 0)); AlwaysAssert(allNearAbs(pos[0].getValue().getValue(), expec, 1e-6), AipsError); // and just check to make the default value of rotating antenna positions // works as expected removeIfNecessary(msfile); reader.reset(new MSFitsInput(msfile, fitsfile)); reader->readFitsFile(); ms = MeasurementSet(msfile); md.reset(new MSMetaData(&ms, 50)); pos = md->getAntennaPositions(vector(1, 0)); AlwaysAssert(allNearAbs(pos[0].getValue().getValue(), expec, 1e-6), AipsError); } } catch (const AipsError& x) { cerr << x.getMesg() << 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-2.4.1/msfits/MSFits/test/tMSFITSOutput.cc000066400000000000000000000063741321422335000216550ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# 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 AipsError& x) { cerr << x.getMesg() << 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-2.4.1/msfits/MSFits/test/tMSFitsSelection.cc000066400000000000000000000104701321422335000224320ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { cout << "ERROR: " << x.getMesg() << endl; } return 1; } casacore-2.4.1/msfits/MSFits/test/tMSFitsSelection.run000077500000000000000000000020161321422335000226510ustar00rootroot00000000000000#!/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. # # $Id$ #----------------------------------------------------------------------------- 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-2.4.1/msfits/MSFits/test/tfits2ms.cc000066400000000000000000000051721321422335000210110ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { cerr << x.getMesg() << 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-2.4.1/msfits/MSFits/test/tfits2ms.run000077500000000000000000000017511321422335000212320ustar00rootroot00000000000000#!/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. # # $Id$ #----------------------------------------------------------------------------- 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-2.4.1/msfits/apps/000077500000000000000000000000001321422335000155415ustar00rootroot00000000000000casacore-2.4.1/msfits/apps/CMakeLists.txt000066400000000000000000000003171321422335000203020ustar00rootroot00000000000000foreach(prog ms2uvfits) add_executable (${prog} ${prog}.cc) target_link_libraries (${prog} casa_msfits ${CASACORE_ARCH_LIBS}) install(TARGETS ${prog} DESTINATION bin) endforeach(prog ms2uvfits) casacore-2.4.1/msfits/apps/ms2uvfits.cc000066400000000000000000000105151321422335000200140ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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 (AipsError x) { cout << x.getMesg() << endl; return 1; } cout << "ms2uvfits normally ended" << endl; return 0; } casacore-2.4.1/msfits/msfits.dox000066400000000000000000000027171321422335000166260ustar00rootroot00000000000000//# 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: aips2-request@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-2.4.1/python/000077500000000000000000000000001321422335000146125ustar00rootroot00000000000000casacore-2.4.1/python/CMakeLists.txt000066400000000000000000000065051321422335000173600ustar00rootroot00000000000000message(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 COMPONENTS python) 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 ) 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-2.4.1/python/Converters.h000066400000000000000000000115371321422335000171240ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: Converters.h,v 1.1 2006/12/13 01:24:15 gvandiep Exp $ #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. //
        // Elements in a numpy array are called array scalars. They do not have a python // type like int, but instead a type like numpy.int32. // The converters can handle such types and convert them correctly to a scalar. // Of course, the converters also can handle sequences containing such types. // // A numpy or numarray scalar array (e.g. array(1.0) is a somewhat // peculiar object that cannot be indexed. It is handled correctly by the // converters and handled as a scalar value. //
        // } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/python/Converters/000077500000000000000000000000001321422335000167445ustar00rootroot00000000000000casacore-2.4.1/python/Converters/PycArray.cc000066400000000000000000000105751321422335000210150ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: PycArray.cc,v 1.4 2006/11/06 00:14:44 gvandiep Exp $ #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-2.4.1/python/Converters/PycArray.h000066400000000000000000000071151321422335000206530ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: PycArray.h,v 1.4 2006/11/06 00:14:44 gvandiep Exp $ #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-2.4.1/python/Converters/PycArray.tcc000066400000000000000000000032301321422335000211670ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: PycArray.tcc,v 1.2 2006/11/06 00:14:44 gvandiep Exp $ #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-2.4.1/python/Converters/PycArrayComCC.h000066400000000000000000000346531321422335000215270ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: PycArrayComCC.h,v 1.3 2006/11/20 23:58:17 gvandiep Exp $ #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 casacore::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, uInt 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 (uInt i=0; i void ArrayCopy::fromPy (T* to, const void* from, uInt 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 (uInt 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, uInt 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, uInt 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, uInt 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, uInt 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, uInt nr) { PyObject** dst = static_cast(to); for (uInt i=0; i::fromPy (String* to, const void* from, uInt nr) { using namespace boost::python; PyObject** src = (PyObject**)from; for (uInt 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 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) { int slen = 0; if (nd > 0) { slen = PyArray_STRIDES(po)[nd-1]; } return ValueHolder (ArrayCopyStr_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-2.4.1/python/Converters/PycArrayComH.h000066400000000000000000000062531321422335000214240ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: PycArrayComH.h,v 1.2 2006/11/07 00:17:23 gvandiep Exp $ // 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, uInt nr); static void fromPy (T* to, const void* from, uInt nr); static Array toArray (const IPosition& shape, void* data, bool copy); }; template <> struct ArrayCopy { static void toPy (void* to, const Complex* from, uInt nr); static void fromPy (Complex* to, const void* from, uInt nr); static Array toArray (const IPosition& shape, void* data, bool copy); }; template <> struct ArrayCopy { static void toPy (void* to, const DComplex* from, uInt nr); static void fromPy (DComplex* to, const void* from, uInt nr); static Array toArray (const IPosition& shape, void* data, bool copy); }; template <> struct ArrayCopy { static void toPy (void* to, const String* from, uInt nr); static void fromPy (String* to, const void* from, uInt nr); static Array toArray (const IPosition& shape, void* data, bool copy); }; // Array ArrayCopyStr_toArray (const IPosition& shape, void* data, uInt slen); // Convert a Casacore array to a Python array object. template boost::python::object makePyArrayObject (casacore::Array const& arr); casacore-2.4.1/python/Converters/PycArrayNP.cc000066400000000000000000000230021321422335000212400ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: PycArrayNP.cc,v 1.2 2006/11/07 00:17:23 gvandiep Exp $ #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, uInt 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); uInt nr = arr.size(); for (uInt 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-2.4.1/python/Converters/PycArrayNP.h000066400000000000000000000120101321422335000210770ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: PycArrayNP.h,v 1.1 2006/11/06 00:14:44 gvandiep Exp $ #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-2.4.1/python/Converters/PycBasicData.cc000066400000000000000000000112551321422335000215460ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: PycBasicData.cc,v 1.4 2007/01/29 04:23:01 mmarquar Exp $ #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(); } 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-2.4.1/python/Converters/PycBasicData.h000066400000000000000000000473451321422335000214210ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: PycBasicData.h,v 1.5 2006/10/25 01:42:13 gvandiep Exp $ #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)) return 0; #endif return obj_ptr; } static void construct( PyObject* obj_ptr, boost::python::converter::rvalue_from_python_stage1_data* data) { #ifdef IS_PY3K PyObject * temp_bytes = PyUnicode_AsEncodedString(obj_ptr, "ASCII", "strict"); // Owned reference char* value; if (temp_bytes != NULL) { value = PyBytes_AS_STRING(temp_bytes); // Borrowed pointer value = strdup(value); Py_DECREF(temp_bytes); } else { boost::python::throw_error_already_set(); } #else const char* value = PyString_AsString(obj_ptr); #endif 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) #ifdef IS_PY3K || PyLong_Check(obj_ptr) #else || PyInt_Check(obj_ptr) #endif || PyLong_Check(obj_ptr) || PyFloat_Check(obj_ptr) || PyComplex_Check(obj_ptr) #ifdef IS_PY3K || PyUnicode_Check(obj_ptr)) { #else || PyString_Check(obj_ptr)) { #endif 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) #ifndef IS_PY3K || PyInt_Check(obj_ptr) #endif || PyLong_Check(obj_ptr) || PyFloat_Check(obj_ptr) || PyComplex_Check(obj_ptr) #ifdef IS_PY3K || PyUnicode_Check(obj_ptr) #else || PyString_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-2.4.1/python/Converters/PycExcp.cc000066400000000000000000000037701321422335000206350ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: PycExcp.cc,v 1.1 2006/10/17 03:33:50 gvandiep Exp $ #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-2.4.1/python/Converters/PycExcp.h000066400000000000000000000032551321422335000204750ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: PycExcp.h,v 1.3 2006/10/17 03:33:50 gvandiep Exp $ #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-2.4.1/python/Converters/PycImport.cc000066400000000000000000000054751321422335000212140ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: PycExcp.cc,v 1.1 2006/10/17 03:33:50 gvandiep Exp $ #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-2.4.1/python/Converters/PycImport.h000066400000000000000000000061611321422335000210470ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: PycExcp.cc,v 1.1 2006/10/17 03:33:50 gvandiep Exp $ #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-2.4.1/python/Converters/PycRecord.cc000066400000000000000000000065231321422335000211530ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: PycRecord.cc,v 1.2 2006/10/17 03:33:50 gvandiep Exp $ #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-2.4.1/python/Converters/PycRecord.h000066400000000000000000000053751321422335000210210ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: PycRecord.h,v 1.2 2006/10/17 03:33:50 gvandiep Exp $ #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-2.4.1/python/Converters/PycValueHolder.cc000066400000000000000000000251751321422335000221530ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: PycValueHolder.cc,v 1.4 2006/11/20 23:56:04 gvandiep Exp $ #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) #ifdef IS_PY3K || PyLong_Check(obj_ptr) #else || PyInt_Check(obj_ptr) #endif || PyLong_Check(obj_ptr) || PyFloat_Check(obj_ptr) || PyComplex_Check(obj_ptr) #ifdef IS_PY3K || PyUnicode_Check(obj_ptr) #else || PyString_Check(obj_ptr) #endif || 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)()); #ifdef IS_PY3K } else if (PyLong_Check(obj_ptr)) { #else } else if (PyInt_Check(obj_ptr)) { #endif return ValueHolder(extract(obj_ptr)()); } else if (PyLong_Check(obj_ptr)) { return ValueHolder(extract(obj_ptr)()); } 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)) { #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; #ifdef IS_PY3K } else if (PyLong_Check (py_elem_obj.ptr())) { #else } else if (PyInt_Check (py_elem_obj.ptr())) { #endif dt = TpInt; } 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; #ifdef IS_PY3K } else if (PyUnicode_Check (py_elem_obj.ptr())) { #else } else if (PyString_Check (py_elem_obj.ptr())) { #endif 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-2.4.1/python/Converters/PycValueHolder.h000066400000000000000000000060741321422335000220120ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: PycValueHolder.h,v 1.2 2006/10/17 03:33:50 gvandiep Exp $ #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-2.4.1/python/Converters/test/000077500000000000000000000000001321422335000177235ustar00rootroot00000000000000casacore-2.4.1/python/Converters/test/CMakeLists.txt000066400000000000000000000005001321422335000224560ustar00rootroot00000000000000include_directories ("..") add_library(tConvert MODULE tConvert.cc) SET_TARGET_PROPERTIES(tConvert PROPERTIES PREFIX "_") target_link_libraries (tConvert casa_python ${Boost_PYTHON_LIBRARY_DEBUG} ${PYTHON_LIBRARIES}) add_test (tConvert ${CMAKE_SOURCE_DIR}/cmake/cmake_assay ./tConvert) add_dependencies(check tConvert) casacore-2.4.1/python/Converters/test/tConvert.cc000066400000000000000000000132471321422335000220450ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: tConvert.cc,v 1.4 2006/11/06 00:14:44 gvandiep Exp $ #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;} 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 ("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-2.4.1/python/Converters/test/tConvert.out000066400000000000000000000102571321422335000222650ustar00rootroot00000000000000Doing numpy/array test ... VH Array [-1 -2] VH Array [211 212] >>> VH Array <<< (0,) VH Array {'shape': [2, 2], 'array': ['abcd', 'c', '12', 'x12']} testarrvh VH Array [ True False] VH Bool True VH Array [ True] VH Array [ True False] bool 1 True testarrvh VH Array [-6 -7] VH Short -6 VH Array [-6] VH Array [-6 -7] Int -6 -6 Int64 -6 -6 ssize -6 -6 Float -6 -6.0 Complex (-6,0) (-6+0j) testarrvh VH Array [5 6] VH uShort 5 VH Array [5] VH Array [5 6] Int 5 5 Int64 5 5 ssize 5 5 Float 5 5.0 Complex (5,0) (5+0j) testarrvh VH Array [-16 -17] VH Short -16 VH Array [-16] VH Array [-16 -17] Int -16 -16 Int64 -16 -16 ssize -16 -16 Float -16 -16.0 Complex (-16,0) (-16+0j) testarrvh VH Array [15 16] VH uShort 15 VH Array [15] VH Array [15 16] Int 15 15 Int64 15 15 ssize 15 15 Float 15 15.0 Complex (15,0) (15+0j) testarrvh VH Array [-26 -27] VH Int -26 VH Array [-26] VH Array [-26 -27] Int -26 -26 Int64 -26 -26 ssize -26 -26 Float -26 -26.0 Complex (-26,0) (-26+0j) testarrvh VH Array [25 26] VH uInt 25 VH Array [25] VH Array [25 26] Int 25 25 Int64 25 25 ssize 25 25 Float 25 25.0 Complex (25,0) (25+0j) testarrvh VH Array [-36 -37] VH Int64 -36 VH Array [-36] VH Array [-36 -37] Int -36 -36 Int64 -36 -36 ssize -36 -36 Float -36 -36.0 Complex (-36,0) (-36+0j) testarrvh VH Array [35 36] VH Int64 35 VH Array [35] VH Array [35 36] Int 35 35 Int64 35 35 ssize 35 35 Float 35 35.0 Complex (35,0) (35+0j) testarrvh VH Array [-46. -47.] VH float -46.0 VH Array [-46.] VH Array [-46. -47.] Float -46 -46.0 Complex (-46,0) (-46+0j) testarrvh VH Array [ 45. 46.] VH double 45.0 VH Array [ 45.] VH Array [ 45. 46.] Float 45 45.0 Complex (45,0) (45+0j) 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) 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) 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 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'] 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 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 {'shape': [2, 2], 'array': ['str1', 'str2', 'str3', 'str4']} 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 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-2.4.1/python/Converters/test/tConvert.py000066400000000000000000000117051321422335000221050ustar00rootroot00000000000000#!/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.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.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 ([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 ({"shape":[2,2],"array":["str1","str2","str3","str4"]}); 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", '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): testarrvh(arr); print t.testbool(arr[0]); def testarri(arr): testarrvh(arr); print t.testint(arr[0]); print t.testint64(arr[0]); print t.testssize(arr[0]); print t.testfloat(arr[0]); print t.testcomplex(arr[0]); def testarrf(arr): testarrvh(arr); print t.testfloat(arr[0]); print t.testcomplex(arr[0]); def testarrc(arr): testarrvh(arr); print t.testcomplex(arr[0]); 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])); 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; t = tConvert(); print "Doing numpy/array test ..." testnp(); # Do other tests. dotest(t) casacore-2.4.1/python/Converters/test/tConvert.run000077500000000000000000000002021321422335000222520ustar00rootroot00000000000000#!/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-2.4.1/python/python.dox000066400000000000000000000030701321422335000166470ustar00rootroot00000000000000//# 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: aips2-request@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-2.4.1/python3/000077500000000000000000000000001321422335000146755ustar00rootroot00000000000000casacore-2.4.1/python3/CMakeLists.txt000066400000000000000000000074731321422335000174500ustar00rootroot00000000000000message(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.5 3.4) find_package(Python REQUIRED) if (PYTHONINTERP_FOUND) if (APPLE) find_package(Boost REQUIRED COMPONENTS python3) else () # NOTE: the name of the python3 version of boost is probably Debian/Ubuntu specific find_package(Boost REQUIRED COMPONENTS python-py${PYTHON_VERSION_MAJOR}${PYTHON_VERSION_MINOR}) endif (APPLE) 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-2.4.1/scimath/000077500000000000000000000000001321422335000147215ustar00rootroot00000000000000casacore-2.4.1/scimath/CMakeLists.txt000066400000000000000000000217071321422335000174700ustar00rootroot00000000000000# # CASA Scimath # add_library (casa_scimath 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/ClassicalStatisticsData.cc Mathematics/Combinatorics.cc Mathematics/FFTPack.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/StatisticsData.cc Mathematics/VectorKernel.cc Mathematics/VanVleck.cc Mathematics/ZScoreCalculator.cc ) 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 RUNTIME DESTINATION bin 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/AutoDiffRep.h Mathematics/AutoDiffRep.tcc Mathematics/AutoDiffX.h Mathematics/ChauvenetCriterionStatistics.h Mathematics/ChauvenetCriterionStatistics.tcc Mathematics/ClassicalStatistics.h Mathematics/ClassicalStatistics.tcc Mathematics/ClassicalStatisticsData.h Mathematics/Combinatorics.h Mathematics/ConstrainedRangeStatistics.h Mathematics/ConstrainedRangeStatistics.tcc Mathematics/ConvolveGridder.h Mathematics/ConvolveGridder.tcc Mathematics/Convolver.h Mathematics/Convolver.tcc Mathematics/DFTServer.h Mathematics/DFTServer.tcc Mathematics/FFTPack.h Mathematics/FFTServer.h Mathematics/FFTW.h Mathematics/FitToHalfStatistics.h Mathematics/FitToHalfStatistics.tcc Mathematics/FitToHalfStatisticsData.h Mathematics/GaussianBeam.h Mathematics/Geometry.h Mathematics/Gridder.h Mathematics/Gridder.tcc Mathematics/HingesFencesStatistics.h Mathematics/HingesFencesStatistics.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/StatsDataProvider.h Mathematics/StatsDataProvider.tcc Mathematics/StatisticsAlgorithm.h Mathematics/StatisticsAlgorithm.tcc Mathematics/StatisticsAlgorithmFactory.h Mathematics/StatisticsAlgorithmFactory.tcc Mathematics/StatisticsData.h Mathematics/StatisticsIncrementer.h Mathematics/StatisticsIncrementer.tcc Mathematics/StatisticsTypes.h Mathematics/StatisticsTypes.tcc Mathematics/StatisticsUtilities.h Mathematics/StatisticsUtilities.tcc Mathematics/VanVleck.h Mathematics/VectorKernel.h Mathematics/ZScoreCalculator.h DESTINATION include/casacore/scimath/Mathematics ) install (FILES Fitting.h Functionals.h Mathematics.h DESTINATION include/casacore/scimath ) add_subdirectory (Fitting/test ${EXCL_ALL}) add_subdirectory (Functionals/test ${EXCL_ALL}) add_subdirectory (Mathematics/test ${EXCL_ALL}) casacore-2.4.1/scimath/Fitting.h000066400000000000000000000207221321422335000165010ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Fitting/000077500000000000000000000000001321422335000163255ustar00rootroot00000000000000casacore-2.4.1/scimath/Fitting/FitGaussian.h000066400000000000000000000210311321422335000207100ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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); // 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(); }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-2.4.1/scimath/Fitting/FitGaussian.tcc000066400000000000000000000511231321422335000212370ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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 = C::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 = C::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 = C::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 = C::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 = C::dbl_max; itsRetryFctr.resize(); itsFirstEstimate.resize(); itsMask.resize(); if (itsDimension*3 && 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; Matrix startparameters(itsNGaussians, ngpars); Matrix solutionparameters(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 = C::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); fitfailure = 0; attempt++; cout << "Attempt " << attempt << ": "; // Perform the fit, and check for problems with the results. try { solution = fitter.fit(pos, f, sigma); } catch (AipsError fittererror) { string errormessage; errormessage = fittererror.getMesg(); cout << "Unsuccessful - Error during fitting." << endl; cout << errormessage << endl; fitfailure = 2; } if (!fitter.converged() && !fitfailure) { fitfailure = 1; cout << "Unsuccessful - Failed to converge." << endl; } if (fitter.converged()) { itsChisquare = fitter.chiSquare(); if (itsChisquare < 0) { cout << "Unsuccessful - ChiSquare of "<< itsChisquare << "is negative." << endl; fitfailure = 3; } else if (isNaN(itsChisquare)){ cout << "Unsuccessful - Convergence to NaN result" << endl; fitfailure = 3; } else { for (uInt g = 0; g < itsNGaussians; g++) { if ((itsDimension == 1 && solution(g*ngpars+2) < 0) || (itsDimension == 2 && (solution(g*ngpars+3) < 0 || solution(g*ngpars+4) < 0)) || (itsDimension == 3 && (solution(g*ngpars+4) < 0 || solution(g*ngpars+5) < 0 || solution(g*ngpars+6) < 0))) { fitfailure = 4; cout << "Unsuccessful - Negative axis widths not permissible."; cout << endl; break; } } if (!fitfailure) { itsRMS = sqrt(itsChisquare / f.nelements()); if (itsRMS > maximumRMS) { cout << "Unsuccessful - RMS of " << itsRMS; cout << " is outside acceptible limits." << endl; fitfailure = 5; } else { cout << "Converged after " << fitter.currentIteration() << " iterations" << endl; } 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++) { solutionparameters(g,p) = solution(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) { cout << "Retry limit reached, "; } else if (timer.real() >= itsMaxTime) { cout << "Time limit reached, "; } cout << "no fit satisfies RMS criterion; using best available fit"; cout << endl; } correctParameters(solutionparameters); return solutionparameters; } // Otherwise, return all zeros cout << "FAILURE - could not find acceptible convergent solution." << endl; itsSuccess = 0; for (uInt g = 0; g < itsNGaussians; g++) { for (uInt p = 0; p < ngpars; p++) { solutionparameters(g,p) = T(0.0); } } // return solutionparameters; } 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) += C::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) += C::pi; while (parameters(g,5) > C::pi) parameters(g,5) -= C::pi; } if (itsDimension == 3) { if (abs(parameters(g,7)) > 1e+5) continue; //spin control while (parameters(g,7) < -C::pi_2) parameters(g,7) += C::pi; while (parameters(g,7) > C::pi_2) parameters(g,7) -= C::pi; if (abs(parameters(g,8)) > 1e+5) continue; //spin control while (parameters(g,8) < -C::pi_2) parameters(g,8) += C::pi; while (parameters(g,8) > C::pi_2) parameters(g,8) -= C::pi; if (abs(parameters(g,7)) > C::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) -= C::pi_2; else parameters(g,7) += C::pi_2; } if (abs(parameters(g,8)) > C::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) -= C::pi_2; } else { parameters(g,8) += C::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-2.4.1/scimath/Fitting/FittingProxy.cc000066400000000000000000000436341321422335000213140ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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-2.4.1/scimath/Fitting/FittingProxy.h000066400000000000000000000131531321422335000211470ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef SCIMATH_FITTINGPROXY_H #define SCIMATH_FITTINGPROXY_H //# Includes #include #include //# Forward declarations namespace casacore { //# NAMESPACE CASACORE - BEGIN class String; template class GenericL2Fit; template class Vector; // 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-2.4.1/scimath/Fitting/GenericL2Fit.h000066400000000000000000000574661321422335000207350ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 Array; 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-2.4.1/scimath/Fitting/GenericL2Fit.tcc000066400000000000000000000517461321422335000212520ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Fitting/LSQFit.cc000066400000000000000000000604051321422335000177430ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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-2.4.1/scimath/Fitting/LSQFit.h000066400000000000000000001127571321422335000176150ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Fitting/LSQFit2.tcc000066400000000000000000001123131321422335000202050ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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(sqrt(std::abs(nceq_p->row(i)[i])), 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])), sqrt(std::abs(nceq_p->row(i+1)[i+1])))* (error_p[CHI2]); } } } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/scimath/Fitting/LSQFit3.cc000066400000000000000000000221631321422335000200250ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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-2.4.1/scimath/Fitting/LSQMatrix.cc000066400000000000000000000066031321422335000204650ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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-2.4.1/scimath/Fitting/LSQMatrix2.cc000066400000000000000000000131121321422335000205400ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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-2.4.1/scimath/Fitting/LSQTraits.h000066400000000000000000000111271321422335000203260ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Fitting/LSQaips.h000066400000000000000000000151571321422335000200230ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Fitting/LSQaips.tcc000066400000000000000000000043111321422335000203330ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Fitting/LinearFit.h000066400000000000000000000231141321422335000203540ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Fitting/LinearFit.tcc000066400000000000000000000057521321422335000207060ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Fitting/LinearFitSVD.h000066400000000000000000000065471321422335000207440ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Fitting/LinearFitSVD.tcc000066400000000000000000000036111321422335000212530ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Fitting/NonLinearFit.h000066400000000000000000000215761321422335000210410ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Fitting/NonLinearFit.tcc000066400000000000000000000056451321422335000213620ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Fitting/NonLinearFitLM.h000066400000000000000000000112531321422335000212610ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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-2.4.1/scimath/Fitting/NonLinearFitLM.tcc000066400000000000000000000072021321422335000216020ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Fitting/test/000077500000000000000000000000001321422335000173045ustar00rootroot00000000000000casacore-2.4.1/scimath/Fitting/test/CMakeLists.txt000066400000000000000000000005151321422335000220450ustar00rootroot00000000000000set (tests dConstraints dLSQFit tFitGaussian tLinearFitSVD tLSQaips tLSQFit tNonLinearFitLM ) foreach (test ${tests}) add_executable (${test} ${test}.cc) target_link_libraries (${test} casa_scimath) add_test (${test} ${CMAKE_SOURCE_DIR}/cmake/cmake_assay ./${test}) add_dependencies(check ${test}) endforeach (test) casacore-2.4.1/scimath/Fitting/test/dConstraints.cc000066400000000000000000000150231321422335000222670ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { cout << x.getMesg() << endl; } return 0; } casacore-2.4.1/scimath/Fitting/test/dLSQFit.cc000066400000000000000000000126331321422335000210660ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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 namespace casacore { template class Matrix; template class Vector; } 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; 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 (AipsError err) { cout << "ERROR: " << err.getMesg() << endl; fail = 1; } cout << " Given Parameters:"; printparameters(components); cout << "Solution Parameters:"; printparameters(solution); 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(); try { solution = fitgauss.fit(pos, f); } catch (AipsError err) { cout << "ERROR: " << err.getMesg() << endl; fail = 1; } cout << " Given Parameters:"; printparameters(components); cout << "Solution Parameters:"; printparameters(solution); 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(); try { solution = fitgauss.fit(pos, f); } catch (AipsError err) { cout << "ERROR: " << err.getMesg() << endl; fail = 1; } cout << " Given Parameters:"; printparameters(components); cout << "Solution Parameters:"; printparameters(solution); 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(); try { solution = fitgauss.fit(pos, f, 0.001); } catch (AipsError err) { cout << "ERROR: " << err.getMesg() << endl; fail = 1; } cout << " Given Parameters:"; printparameters(components); cout << "Solution Parameters:"; printparameters(solution); 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(); try { solution = fitgauss.fit(pos, f, 0.01, 256); } catch (AipsError err) { cout << "ERROR: " << err.getMesg() << endl; fail = 1; } cout << " Given Parameters:"; printparameters(components); cout << "Solution Parameters:"; printparameters(solution); 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-2.4.1/scimath/Fitting/test/tLSQFit.cc000066400000000000000000001271001321422335000211020ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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 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-2.4.1/scimath/Fitting/test/tNonLinearFitLM.cc000066400000000000000000001030651321422335000225650ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Functionals/000077500000000000000000000000001321422335000172065ustar00rootroot00000000000000casacore-2.4.1/scimath/Functionals/AbstractFunctionFactory.h000066400000000000000000000047771321422335000241770ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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-2.4.1/scimath/Functionals/ArraySampledFunctional.h000066400000000000000000000130171321422335000237700ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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-2.4.1/scimath/Functionals/ArraySampledFunctional.tcc000066400000000000000000000071621321422335000243160ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Functionals/Chebyshev.h000066400000000000000000000315601321422335000213040ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //#! ======================================================================== //# $Id$ #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-2.4.1/scimath/Functionals/Chebyshev.tcc000066400000000000000000000060701321422335000216240ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Functionals/ChebyshevParam.h000066400000000000000000000523771321422335000222760ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //#! ======================================================================== //# $Id$ #ifndef SCIMATH_CHEBYSHEVPARAM_H #define SCIMATH_CHEBYSHEVPARAM_H #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations template class Vector; 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-2.4.1/scimath/Functionals/ChebyshevParam.tcc000066400000000000000000000302721321422335000226060ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef SCIMATH_CHEBYSHEVPARAM_TCC #define SCIMATH_CHEBYSHEVPARAM_TCC //# Includes #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-2.4.1/scimath/Functionals/Combi2Function.tcc000066400000000000000000000042651321422335000225310ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Functionals/CombiFunction.h000066400000000000000000000171251321422335000221240ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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-2.4.1/scimath/Functionals/CombiFunction.tcc000066400000000000000000000033351321422335000224440ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Functionals/CombiParam.h000066400000000000000000000145151321422335000213770ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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-2.4.1/scimath/Functionals/CombiParam.tcc000066400000000000000000000060421321422335000217150ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Functionals/CompiledFunction.h000066400000000000000000000141541321422335000226260ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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-2.4.1/scimath/Functionals/CompiledFunction.tcc000066400000000000000000000173711321422335000231540ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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; } 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> (C::ln2)); break; case FuncExprData::EXP10: exec_p.back() = exp(exec_p.back()* static_cast::BaseType> (C::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>(C::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> (C::pi))); } else { exec_p.back() *= static_cast::BaseType> (C::pi); } break; } case FuncExprData::EE: { if (pos->state.argcnt == 0) { exec_p.push_back(T(static_cast::BaseType> (C::e))); } else { exec_p.back() *= static_cast::BaseType> (C::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-2.4.1/scimath/Functionals/CompiledParam.h000066400000000000000000000130221321422335000220720ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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-2.4.1/scimath/Functionals/CompiledParam.tcc000066400000000000000000000056371321422335000224310ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Functionals/Compound2Function.tcc000066400000000000000000000111311321422335000232520ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Functionals/CompoundFunction.h000066400000000000000000000251611321422335000226560ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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-2.4.1/scimath/Functionals/CompoundFunction.tcc000066400000000000000000000043761321422335000232050ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Functionals/CompoundParam.h000066400000000000000000000175111321422335000221310ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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-2.4.1/scimath/Functionals/CompoundParam.tcc000066400000000000000000000103301321422335000224430ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Functionals/ConstantND.tcc000066400000000000000000000033641321422335000217220ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Functionals/ConstantNDParam.h000066400000000000000000000123131321422335000223530ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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-2.4.1/scimath/Functionals/ConstantNDParam.tcc000066400000000000000000000037061321422335000227030ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Functionals/DiracDFunction.h000066400000000000000000000111411321422335000222110ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Functionals/DiracDFunction.tcc000066400000000000000000000034031321422335000225350ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Functionals/DiracDParam.h000066400000000000000000000074411321422335000214740ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Functionals/DiracDParam.tcc000066400000000000000000000042661321422335000220200ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Functionals/EclecticFunctionFactory.h000066400000000000000000000110401321422335000241240ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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-2.4.1/scimath/Functionals/EclecticFunctionFactory.tcc000066400000000000000000000060341321422335000244550ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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-2.4.1/scimath/Functionals/EvenPolynomial.h000066400000000000000000000155641321422335000223330ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Functionals/EvenPolynomial.tcc000066400000000000000000000033661321422335000226520ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Functionals/EvenPolynomial2.tcc000066400000000000000000000045071321422335000227320ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Functionals/EvenPolynomialParam.h000066400000000000000000000126151321422335000233060ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef SCIMATH_EVENPOLYNOMIALPARAM_H #define SCIMATH_EVENPOLYNOMIALPARAM_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward declarations template class Vector; // 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-2.4.1/scimath/Functionals/EvenPolynomialParam.tcc000066400000000000000000000045201321422335000236240ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Functionals/FuncExprData.cc000066400000000000000000000177701321422335000220550ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ //# 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-2.4.1/scimath/Functionals/FuncExprData.h000066400000000000000000000144541321422335000217130ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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-2.4.1/scimath/Functionals/FuncExpression.cc000066400000000000000000000423201321422335000224710ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ //# 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; } 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()*C::ln2); break; case FuncExprData::EXP10: exec_p.back() = exp(exec_p.back()*C::ln10); break; case FuncExprData::LOG: exec_p.back() = log(exec_p.back()); break; case FuncExprData::LOG2: exec_p.back() = log(exec_p.back())/C::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(C::pi); else exec_p.back() *= C::pi; break; } case FuncExprData::EE: { if (pos->state.argcnt == 0) exec_p.push_back(C::e); else exec_p.back() *= C::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-2.4.1/scimath/Functionals/FuncExpression.h000066400000000000000000000132321321422335000223330ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #ifndef SCIMATH_FUNCEXPRESSION_H #define SCIMATH_FUNCEXPRESSION_H //# Includes #include #include #include #include //# Forward Declarations #include namespace casacore { //# NAMESPACE CASACORE - BEGIN class MUString; template class Vector; // 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-2.4.1/scimath/Functionals/Function.h000066400000000000000000000350171321422335000211520ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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) {} Function(const Function &other) : Functional::ArgType, U> (other), Functional::ArgType>, U>(other), param_p(other.param_p), arg_p(other.arg_p), parset_p(other.parset_p), 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-2.4.1/scimath/Functionals/Function.tcc000066400000000000000000000061701321422335000214720ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Functionals/Function1D.h000066400000000000000000000067711321422335000213440ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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) {} Function1D(const Function1D &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-2.4.1/scimath/Functionals/FunctionFactoryErrors.cc000066400000000000000000000033451321422335000240340ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #include namespace casacore { //# NAMESPACE CASACORE - BEGIN const String InvalidSerializationError::preamble("Invalid function description in record: "); FunctionFactoryError::~FunctionFactoryError() throw() { } UnrecognizedFunctionError::~UnrecognizedFunctionError( ) throw() { } InvalidSerializationError::~InvalidSerializationError() throw() { } FieldNotFoundError::~FieldNotFoundError() throw() { } } //# NAMESPACE CASACORE - END casacore-2.4.1/scimath/Functionals/FunctionFactoryErrors.h000066400000000000000000000057511321422335000237010ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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() throw(); }; 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() throw(); 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() throw(); 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() throw(); private: String fname; }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/scimath/Functionals/FunctionHolder.h000066400000000000000000000161761321422335000223150ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 PtrHolder > hold_p; // Aids (only filled after a succesful to/fromRecord // mutable Types nf_p; mutable Int order_p; mutable String text_p; mutable PtrHolder 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-2.4.1/scimath/Functionals/FunctionHolder.tcc000066400000000000000000000371231321422335000226320ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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() : hold_p(), mode_p(), nam_p(N_Types), isFilled(False) {} template FunctionHolder::FunctionHolder(const Function &in) : hold_p(in.clone()), mode_p(), nam_p(N_Types), isFilled(False) { if (in.hasMode()) { mode_p.set(new Record(RecordInterface::Variable)); in.getMode( *(mode_p.ptr()) ); } } template FunctionHolder::FunctionHolder(const FunctionHolder &other) : hold_p(), mode_p(), nam_p(N_Types), isFilled(False) { if (other.hold_p.ptr()) hold_p.set(other.hold_p.ptr()->clone()); if (other.mode_p.ptr()) mode_p.set(other.mode_p.ptr()->clone()); } //# Destructor template FunctionHolder::~FunctionHolder() {} //# Operators template FunctionHolder &FunctionHolder:: operator=(const FunctionHolder &other) { if (this != &other) { if (other.hold_p.ptr()) { hold_p.set(other.hold_p.ptr()->clone()); } else { hold_p.clear(); } if (other.mode_p.ptr()) { mode_p.set(other.mode_p.ptr()->clone()); } else { mode_p.clear(); } } return *this; } //# Member Functions template Bool FunctionHolder::isEmpty() const { return (!hold_p.ptr()); } template const Vector &FunctionHolder::names() const { init(); return nam_p; } template const Function &FunctionHolder::asFunction() const { if (!hold_p.ptr()) { throw(AipsError("Empty FunctionHolder argument for asFunction")); } return *hold_p.ptr(); } template Bool FunctionHolder::addFunction(const Function &fnc) { if (nf_p == COMBINE) { dynamic_cast &>(*hold_p.ptr()).addFunction(fnc); } else if (nf_p == COMPOUND) { dynamic_cast &>(*hold_p.ptr()).addFunction(fnc); } else return False; return True; } template typename FunctionHolder::Types FunctionHolder::type() const { if (!hold_p.ptr()) { 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.clear(); Function *fn(0); if (!getRecord(error, fn, in)) { delete fn; fn = 0; return False; } hold_p.set(fn); return True; } template template Bool FunctionHolder::getRecord(String &error, Function *&fn, const RecordInterface &in) { if (in.isDefined(String("type")) && in.isDefined(String("order")) && in.type(in.idToNumber(RecordFieldId("order"))) == TpInt && (in.type(in.idToNumber(RecordFieldId("type"))) == TpString || (in.type(in.idToNumber(RecordFieldId("type"))) == TpInt && in.isDefined(String("ndim")) && in.isDefined(String("npar")) && in.isDefined(String("params")) && in.type(in.idToNumber(RecordFieldId("ndim"))) == TpInt && in.type(in.idToNumber(RecordFieldId("npar"))) == TpInt && (in.type(in.idToNumber(RecordFieldId("params"))) == TpArrayDouble || in.type(in.idToNumber(RecordFieldId("params"))) == TpArrayDComplex)))) { 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("nfunc"))) == TpInt && 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")) && (in.type(in.idToNumber(RecordFieldId("params"))) == TpArrayDouble || in.type(in.idToNumber(RecordFieldId("params"))) == TpArrayDComplex)) { Vector params; in.get(RecordFieldId("params"), params); setParameters(fn, params); } if (in.isDefined(String("masks")) && in.type(in.idToNumber(RecordFieldId("masks"))) == TpArrayBool) { Vector masks; in.get(RecordFieldId("masks"), masks); for (uInt i=0; inparameters(); ++i) fn->mask(i) = masks[i]; } return True; } error += String("Illegal Function record in " "FunctionHolder::fromRecord\n"); 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.set(fn); return True; } delete fn; fn = 0; return False; } template Bool FunctionHolder::toRecord(String &error, RecordInterface &out) const { if (hold_p.ptr() && putType(error, out)) { out.define(RecordFieldId("ndim"), static_cast(hold_p.ptr()->ndim())); out.define(RecordFieldId("npar"), static_cast(hold_p.ptr()->nparameters())); out.define(RecordFieldId("params"), hold_p.ptr()->parameters().getParameters()); out.define(RecordFieldId("masks"), hold_p.ptr()->parameters().getParamMasks()); Record mode; hold_p.ptr()->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.ptr())->nFunctions(); } else { x = dynamic_cast *> (hold_p.ptr())->nFunctions(); } out.define("nfunc", x); Record func; for (Int i=0; i fn(dynamic_cast *> (hold_p.ptr())->function(i)); if (!fn.toRecord(error, fnc)) return False; } else { FunctionHolder fn(dynamic_cast *> (hold_p.ptr())->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.ptr())) { nf_p = GAUSSIAN1D; } else if (dynamic_cast *>(hold_p.ptr())) { nf_p = GAUSSIAN2D; } else if (dynamic_cast *>(hold_p.ptr())) { nf_p = GAUSSIAN3D; } else if (dynamic_cast *>(hold_p.ptr())) { nf_p = GAUSSIANND; order_p = Int(-3.0+sqrt(1.0+8.0*hold_p.ptr()->nparameters())+0.1)/2; } else if (dynamic_cast *>(hold_p.ptr())) { nf_p = HYPERPLANE; order_p = hold_p.ptr()->nparameters(); } else if (dynamic_cast *>(hold_p.ptr())) { nf_p = POLYNOMIAL; order_p = hold_p.ptr()->nparameters()-1; } else if (dynamic_cast *>(hold_p.ptr())) { nf_p = EVENPOLYNOMIAL; order_p = 2*hold_p.ptr()->nparameters()-1; } else if (dynamic_cast *>(hold_p.ptr())) { nf_p = ODDPOLYNOMIAL; order_p = 2*hold_p.ptr()->nparameters()-1; } else if (dynamic_cast *>(hold_p.ptr())) { nf_p = SINUSOID1D; } else if (dynamic_cast *>(hold_p.ptr())) { nf_p = CHEBYSHEV; order_p = hold_p.ptr()->nparameters()-1; } else if (dynamic_cast *>(hold_p.ptr())) { nf_p = BUTTERWORTH; } else if (dynamic_cast *>(hold_p.ptr())) { nf_p = COMBINE; } else if (dynamic_cast *>(hold_p.ptr())) { nf_p = COMPOUND; } else if (dynamic_cast *>(hold_p.ptr())) { nf_p = COMPILED; text_p = dynamic_cast *>(hold_p.ptr())-> 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.set(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; } /// hold_p.clear(); 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.ptr()) fn = (new Chebyshev(order_p, *(mode_p.ptr()) )); else fn = (new Chebyshev(order_p)); break; case BUTTERWORTH: if (mode_p.ptr()) fn = (new SimButterworthBandpass(*(mode_p.ptr()) )); 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-2.4.1/scimath/Functionals/FunctionMarshallable.h000066400000000000000000000070621321422335000234610ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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-2.4.1/scimath/Functionals/FunctionOrder.h000066400000000000000000000123161321422335000221430ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Functionals/FunctionOrder.tcc000066400000000000000000000154661321422335000224760ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Functionals/FunctionParam.h000066400000000000000000000160631321422335000221330ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Functionals/FunctionParam.tcc000066400000000000000000000113611321422335000224510ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Functionals/FunctionWrapper.h000066400000000000000000000126271321422335000225150ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef SCIMATH_FUNCTIONWRAPPER_H #define SCIMATH_FUNCTIONWRAPPER_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward declarations template class Vector; 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 CountedPtr > 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-2.4.1/scimath/Functionals/FunctionWrapper.tcc000066400000000000000000000101041321422335000230230ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #ifndef SCIMATH_FUNCTIONWRAPPER_TCC #define SCIMATH_FUNCTIONWRAPPER_TCC //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Constructors template FunctionWrapper::FunctionWrapper() : WrapperParam(0), doit_p(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-2.4.1/scimath/Functionals/FunctionalProxy.cc000066400000000000000000000172321321422335000226660ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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-2.4.1/scimath/Functionals/FunctionalProxy.h000066400000000000000000000052571321422335000225340ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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-2.4.1/scimath/Functionals/GNoiseFunction.h000066400000000000000000000076421321422335000222620ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Functionals/GNoiseFunction.tcc000066400000000000000000000032721321422335000225770ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Functionals/GNoiseParam.h000066400000000000000000000070021321422335000215230ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Functionals/GNoiseParam.tcc000066400000000000000000000043371321422335000220550ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Functionals/Gaussian1D.h000066400000000000000000000241111321422335000213150ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Functionals/Gaussian1D.tcc000066400000000000000000000033621321422335000216440ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Functionals/Gaussian1D2.tcc000066400000000000000000000055361321422335000217330ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Functionals/Gaussian1DParam.h000066400000000000000000000162431321422335000223050ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Functionals/Gaussian1DParam.tcc000066400000000000000000000064511321422335000226270ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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(C::_1_sqrtpi); } template void Gaussian1DParam::setFlux(const T &flux) { param_p[HEIGHT] = flux*T(C::_1_sqrtpi)/abs(param_p[WIDTH])/fwhm2int; } } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/scimath/Functionals/Gaussian2D.h000066400000000000000000000331031321422335000213170ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef SCIMATH_GAUSSIAN2D_H #define SCIMATH_GAUSSIAN2D_H //# Includes #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward declarations template class Vector; // 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-2.4.1/scimath/Functionals/Gaussian2D.tcc000066400000000000000000000041251321422335000216430ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Functionals/Gaussian2D2.tcc000066400000000000000000000111751321422335000217300ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Functionals/Gaussian2DParam.h000066400000000000000000000265051321422335000223100ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #ifndef SCIMATH_GAUSSIAN2DPARAM_H #define SCIMATH_GAUSSIAN2DPARAM_H #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward declarations template class Vector; // 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-2.4.1/scimath/Functionals/Gaussian2DParam.tcc000066400000000000000000000176601321422335000226340ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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(C::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(C::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(C::pi)); else pa = fmod(param_p[PANGLE]+T(C::pi_2), T(C::pi)); if (pa < T(0.0)) pa += T(C::pi); return pa; } template void Gaussian2DParam::setPA(const T &pa) { if (abs(pa) > T(C::_2pi)) { 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(C::pi_2); theCpa = cos(param_p[PANGLE]); theSpa = sin(param_p[PANGLE]); thePA = param_p[PANGLE]; } } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/scimath/Functionals/Gaussian3D.h000066400000000000000000000267111321422335000213270ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef SCIMATH_GAUSSIAN3D_H #define SCIMATH_GAUSSIAN3D_H #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations. template class Vector; // 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-2.4.1/scimath/Functionals/Gaussian3D.tcc000066400000000000000000000065141321422335000216500ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Functionals/Gaussian3D2.tcc000066400000000000000000000217651321422335000217370ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Functionals/Gaussian3DParam.h000066400000000000000000000233351321422335000223070ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #ifndef SCIMATH_GAUSSIAN3DPARAM_H #define SCIMATH_GAUSSIAN3DPARAM_H #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations. template class Vector; // 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-2.4.1/scimath/Functionals/Gaussian3DParam.tcc000066400000000000000000000203211321422335000226210ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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(C::pi*sqrt(C::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(C::pi*sqrt(C::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(C::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(C::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-2.4.1/scimath/Functionals/GaussianND.h000066400000000000000000000231161321422335000213560ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Functionals/GaussianND.tcc000066400000000000000000000042151321422335000216770ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Functionals/GaussianNDParam.tcc000066400000000000000000000220261321422335000226600ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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(C::_2pi),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(C::_2pi),-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(C::_2pi),-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(C::_2pi),-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(C::_2pi),-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-2.4.1/scimath/Functionals/HyperPlaneParam.tcc000066400000000000000000000036071321422335000227370ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Functionals/Interpolate1D.h000066400000000000000000000206151321422335000220360ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# $Id$ #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-2.4.1/scimath/Functionals/Interpolate1D.tcc000066400000000000000000000266771321422335000223760ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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, nElements); return x; } template Vector Interpolate1D:: getY() const { Vector y(yValues, 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-2.4.1/scimath/Functionals/KaiserBFunction.h000066400000000000000000000107501321422335000224100ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Functionals/KaiserBFunction.tcc000066400000000000000000000041131321422335000227260ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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(C::pi) * param_p[KBPAR]; T x2 = T(C::pi) * sqrt(par2 - T(1.0)); T x3 = T(C::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(C::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-2.4.1/scimath/Functionals/KaiserBParam.h000066400000000000000000000072201321422335000216610ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Functionals/KaiserBParam.tcc000066400000000000000000000042221321422335000222020ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Functionals/Lorentzian1D.h000066400000000000000000000243671321422335000217050ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Functionals/Lorentzian1D.tcc000066400000000000000000000034011321422335000222110ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Functionals/Lorentzian1D2.tcc000066400000000000000000000055361321422335000223060ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Functionals/Lorentzian1DParam.h000066400000000000000000000163171321422335000226620ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Functionals/Lorentzian1DParam.tcc000066400000000000000000000064221321422335000232000ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Functionals/MarshButterworthBandpass.h000066400000000000000000000132241321422335000243610ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //#! ======================================================================== //# $Id$ #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-2.4.1/scimath/Functionals/MarshButterworthBandpass.tcc000066400000000000000000000063741321422335000247130ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //#! ======================================================================== //# $Id$ #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-2.4.1/scimath/Functionals/MarshallableChebyshev.h000066400000000000000000000164101321422335000236110ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //#! ======================================================================== //# $Id$ #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-2.4.1/scimath/Functionals/MarshallableChebyshev.tcc000066400000000000000000000067001321422335000241340ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //#! ======================================================================== //# $Id$ #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-2.4.1/scimath/Functionals/OddPolynomial.h000066400000000000000000000154731321422335000221430ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Functionals/OddPolynomial.tcc000066400000000000000000000033661321422335000224630ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Functionals/OddPolynomial2.tcc000066400000000000000000000045111321422335000225360ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Functionals/OddPolynomialParam.h000066400000000000000000000126361321422335000231220ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef SCIMATH_ODDPOLYNOMIALPARAM_H #define SCIMATH_ODDPOLYNOMIALPARAM_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward declarations template class Vector; // 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-2.4.1/scimath/Functionals/OddPolynomialParam.tcc000066400000000000000000000044751321422335000234460ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Functionals/PoissonFunction.h000066400000000000000000000155021321422335000225220ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: Array.h 21545 2015-01-22 19:36:35Z gervandiepen $ #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-2.4.1/scimath/Functionals/PoissonFunction.tcc000066400000000000000000000035641321422335000230510ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: Array.h 21545 2015-01-22 19:36:35Z gervandiepen $ #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-2.4.1/scimath/Functionals/PoissonFunction2.tcc000066400000000000000000000051371321422335000231310ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: Array.h 21545 2015-01-22 19:36:35Z gervandiepen $ #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-2.4.1/scimath/Functionals/PoissonParam.tcc000066400000000000000000000042751321422335000223240ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: Array.h 21545 2015-01-22 19:36:35Z gervandiepen $ #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-2.4.1/scimath/Functionals/Polynomial.h000066400000000000000000000163371321422335000215140ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Functionals/Polynomial.tcc000066400000000000000000000037351321422335000220340ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Functionals/Polynomial2.tcc000066400000000000000000000044271321422335000221150ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Functionals/PolynomialParam.h000066400000000000000000000127231321422335000224700ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef SCIMATH_POLYNOMIALPARAM_H #define SCIMATH_POLYNOMIALPARAM_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward declarations template class Vector; // 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-2.4.1/scimath/Functionals/PolynomialParam.tcc000066400000000000000000000044241321422335000230110ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Functionals/PowerLogarithmicPolynomial.h000066400000000000000000000171211321422335000247040ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: Polynomial.h 21024 2011-03-01 11:46:18Z gervandiepen $ #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-2.4.1/scimath/Functionals/PowerLogarithmicPolynomial.tcc000066400000000000000000000041431321422335000252260ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: Polynomial.tcc 20253 2008-02-23 15:15:00Z gervandiepen $ #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-2.4.1/scimath/Functionals/PowerLogarithmicPolynomial2.tcc000066400000000000000000000050371321422335000253130ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: Polynomial2.tcc 20253 2008-02-23 15:15:00Z gervandiepen $ #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-2.4.1/scimath/Functionals/PowerLogarithmicPolynomialParam.h000066400000000000000000000125061321422335000256670ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: PolynomialParam.h 21024 2011-03-01 11:46:18Z gervandiepen $ #ifndef SCIMATH_POWERLOGARITHMICPOLYNOMIALPARAM_H #define SCIMATH_POWERLOGARITHMICPOLYNOMIALPARAM_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward declarations template class Vector; // 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-2.4.1/scimath/Functionals/PowerLogarithmicPolynomialParam.tcc000066400000000000000000000064171321422335000262150ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: PolynomialParam.tcc 19879 2007-02-15 03:52:50Z Malte.Marquarding $ #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-2.4.1/scimath/Functionals/SPolynomial.h000066400000000000000000000110201321422335000216170ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Functionals/SPolynomial.tcc000066400000000000000000000033701321422335000221520ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Functionals/SPolynomialParam.h000066400000000000000000000131401321422335000226050ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef SCIMATH_SPOLYNOMIALPARAM_H #define SCIMATH_SPOLYNOMIALPARAM_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward declarations template class Vector; // 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-2.4.1/scimath/Functionals/SPolynomialParam.tcc000066400000000000000000000050251321422335000231320ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Functionals/ScalarSampledFunctional.h000066400000000000000000000134621321422335000241230ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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-2.4.1/scimath/Functionals/ScalarSampledFunctional.tcc000066400000000000000000000056651321422335000244530ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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) { } 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-2.4.1/scimath/Functionals/SerialHelper.cc000066400000000000000000000407411321422335000221020ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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 (AipsError (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-2.4.1/scimath/Functionals/SerialHelper.h000066400000000000000000000140141321422335000217360ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #ifndef SCIMATH_SERIALHELPER_H #define SCIMATH_SERIALHELPER_H #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template class Array; 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-2.4.1/scimath/Functionals/SimButterworthBandpass.h000066400000000000000000000242711321422335000240430ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef SCIMATH_SIMBUTTERWORTHBANDPASS_H #define SCIMATH_SIMBUTTERWORTHBANDPASS_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations template class Vector; // // 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-2.4.1/scimath/Functionals/SimButterworthBandpass.tcc000066400000000000000000000113521321422335000243610ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Functionals/SincFunction.h000066400000000000000000000113631321422335000217650ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Functionals/SincFunction.tcc000066400000000000000000000034351321422335000223100ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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(C::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-2.4.1/scimath/Functionals/SincParam.h000066400000000000000000000076531321422335000212470ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Functionals/SincParam.tcc000066400000000000000000000046421321422335000215640ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Functionals/Sinusoid1D.h000066400000000000000000000215341321422335000213460ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Functionals/Sinusoid1D.tcc000066400000000000000000000033551321422335000216710ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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(C::_2pi)*(x[0] - param_p[X0])/param_p[PERIOD]); } //# Member functions } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/scimath/Functionals/Sinusoid1D2.tcc000066400000000000000000000071441321422335000217530ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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>(C::_2pi) * (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>(C::_2pi) * 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-2.4.1/scimath/Functionals/Sinusoid1DParam.h000066400000000000000000000133161321422335000223260ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Functionals/Sinusoid1DParam.tcc000066400000000000000000000051451321422335000226510ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Functionals/SpecificFunctionFactory.h000066400000000000000000000055061321422335000241500ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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-2.4.1/scimath/Functionals/UnaryFunction.h000066400000000000000000000113751321422335000221720ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Functionals/UnaryFunction.tcc000066400000000000000000000035011321422335000225040ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Functionals/UnaryParam.h000066400000000000000000000077161321422335000214510ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Functionals/UnaryParam.tcc000066400000000000000000000047211321422335000217640ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Functionals/WrapperBase.h000066400000000000000000000062251321422335000215770ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Functionals/WrapperData.h000066400000000000000000000300701321422335000215710ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Functionals/WrapperParam.h000066400000000000000000000070541321422335000217660ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Functionals/WrapperParam.tcc000066400000000000000000000041071321422335000223040ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Functionals/test/000077500000000000000000000000001321422335000201655ustar00rootroot00000000000000casacore-2.4.1/scimath/Functionals/test/CMakeLists.txt000066400000000000000000000011121321422335000227200ustar00rootroot00000000000000set (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) target_link_libraries (${test} casa_scimath) add_test (${test} ${CMAKE_SOURCE_DIR}/cmake/cmake_assay ./${test}) add_dependencies(check ${test}) endforeach (test) casacore-2.4.1/scimath/Functionals/test/dFunction.cc000066400000000000000000000234101321422335000224250ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ // //# 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 (AipsError ex) { cerr << "Exception: " << ex.getMesg() << endl; exit(1); } cout << "OK" << endl; return 0; } casacore-2.4.1/scimath/Functionals/test/tCombiFunction.cc000066400000000000000000000116031321422335000234200ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Functionals/test/tCompoundFunction.cc000066400000000000000000000076571321422335000241710ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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/C::e/C::e/C::e/C::e)); Double xvec = 1.0; AlwaysAssertExit(near(sumfunc(xvec), 1.0*1.0 + 1.0/C::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/C::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/C::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-2.4.1/scimath/Functionals/test/tConstantND.cc000066400000000000000000000121021321422335000226670ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { cout << "Exception : " << x.getMesg() << endl; } cout << "OK" << endl; return 0; } casacore-2.4.1/scimath/Functionals/test/tFuncExpression.cc000066400000000000000000000110671321422335000236400ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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 (AipsError x) { cerr << x.getMesg() << endl; cout << "FAIL" << endl; return 1; } catch (...) { cerr << "Exception not derived from AipsError" << endl; cout << "FAIL" << endl; return 2; } } casacore-2.4.1/scimath/Functionals/test/tFuncExpression.out000066400000000000000000000344551321422335000240700ustar00rootroot00000000000000------------ 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-2.4.1/scimath/Functionals/test/tFunctionHolder.cc000066400000000000000000000124401321422335000236040ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Functionals/test/tFunctionHolder.out000066400000000000000000000066451321422335000240400ustar00rootroot00000000000000---------------- 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-2.4.1/scimath/Functionals/test/tFunctionOrder.cc000066400000000000000000000044161321422335000234460ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Functionals/test/tFunctionOrder.out000066400000000000000000000005651321422335000236710ustar00rootroot00000000000000---------------- test FunctionOrder --------------- x: [[], [], "", 0, [], [], []] To: 1 From: 1: [[], [], "", 0, [], [], []] ----------------------------------------------------- x: [[0, 5], [], "", 0, [], [], []] To: 1 From: 1: [[0, 5], [], "", 0, [], [], []] ----------------------------------------------------- ----------------------------------------------------- OK casacore-2.4.1/scimath/Functionals/test/tFunctionWrapper.cc000066400000000000000000000062141321422335000240110ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Functionals/test/tGaussian1D.cc000066400000000000000000000223011321422335000226150ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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(C::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(C::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(C::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(C::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(C::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(C::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-2.4.1/scimath/Functionals/test/tGaussian2D.cc000066400000000000000000000212261321422335000226230ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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 = C::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 (AipsError x) { cerr << x.getMesg() << endl; cout << "Failed" << endl; return 1; } } casacore-2.4.1/scimath/Functionals/test/tGaussian3D.cc000066400000000000000000000077111321422335000226270ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Functionals/test/tGaussianND.cc000066400000000000000000000307261321422335000226640ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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.*C::pi),1E-5)) { failed = True; cout << "Expected value for f(" << z << ") is " << exp(-(z(0)*z(0)+z(1)*z(1))/2)/(C::_2pi) << " 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(C::_2pi),1E-5)) { failed = True; cout << "Expected value for g(" << z << ") is " << exp(-(z(0)*z(0)/2))/sqrt(C::_2pi) << " 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(C::_2pi),Float(1.5)), 1E-5)) { failed = True; cout << "Failed (value was " << sum*inc*inc*inc << " not " << pow(Float(C::_2pi),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, C::_2pi, 1E-5)) { failed = True; cout << "Failed (value was " << sum*inc*inc << " not " << C::_2pi << ")"; } 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(C::_2pi,1.5) , 1E-4)) { failed = True; cout << "Failed (value was " << sum*inc*inc*inc << " not " << height*sqrt(variance(0)*variance(1)*variance(2)) *pow(C::_2pi,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 (AipsError x) { cerr << x.getMesg() << endl; cout << "FAIL" << endl; return 1; } } casacore-2.4.1/scimath/Functionals/test/tHyperPlane.cc000066400000000000000000000111571321422335000227340ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { cout << "Exception : " << x.getMesg() << endl; } cout << "OK" << endl; return 0; } casacore-2.4.1/scimath/Functionals/test/tInterpolate1D.cc000066400000000000000000000224001321422335000233310ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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; x.toBlock(bx); Block by; y.toBlock(by); 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-2.4.1/scimath/Functionals/test/tPoisson.cc000066400000000000000000000111411321422335000223100ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: HostInfoDarwin.h 21521 2014-12-10 08:06:42Z gervandiepen $ #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-2.4.1/scimath/Functionals/test/tPolynomial.cc000066400000000000000000000131401321422335000230020ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Functionals/test/tPowerLogarithmicPolynomial.cc000066400000000000000000000136211321422335000262060ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: tPowerLogarithmicPolynomial.cc $ #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-2.4.1/scimath/Functionals/test/tSPolynomial.cc000066400000000000000000000120371321422335000231310ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Functionals/test/tSampledFunctional.cc000066400000000000000000000117031321422335000242720ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Functionals/test/tSimButterworthBandpass.cc000066400000000000000000000074351321422335000253470ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError ex) { cerr << "Exception: " << ex.getMesg() << endl; exit(1); } cout << "OK" << endl; return 0; } casacore-2.4.1/scimath/Functionals/test/tSinusoid1D.cc000066400000000000000000000131111321422335000226370ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 = C::_2pi * 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*C::_2pi*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*C::_2pi*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-2.4.1/scimath/Mathematics.h000066400000000000000000000105651321422335000173400ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef SCIMATH_MATHEMATICS_H #define SCIMATH_MATHEMATICS_H #include #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 which are done using // the functions in the FFTPack // class. The FFTPack class also contains functions for doing // one-dimensional sine, cosine, and real-symmetric 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-2.4.1/scimath/Mathematics/000077500000000000000000000000001321422335000171605ustar00rootroot00000000000000casacore-2.4.1/scimath/Mathematics/AutoDiff.h000066400000000000000000000336121321422335000210370ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #ifndef SCIMATH_AUTODIFF_H #define SCIMATH_AUTODIFF_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward declarations template class Vector; template class AutoDiff; // // 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. The actual structure of the // data block used by AutoDiff is described in // AutoDiffRep. //
        // // // // // 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); // Assignment 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 pointer to the structure of value and derivatives. // AutoDiffRep *theRep() { return rep_p; } const AutoDiffRep *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 an AutoDiff // Vector derivatives() const; 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 rep_p->grad_p(which); } const T &derivative(uInt which) const { return rep_p->grad_p(which); } T &deriv(uInt which) { return rep_p->grad_p[which]; } const T &deriv(uInt which) const { return rep_p->grad_p[which]; } // // Return total number of derivatives uInt nDerivatives() const { return rep_p->nd_p; } // Is it a constant, i.e., with zero derivatives? Bool isConstant() const { return rep_p->nd_p == 0; } // Indicate that we are going to use a temporary value for the last time // (e.g. at the of a function returning by value). This way superfluous // copying can be circumvented. const AutoDiff &ref() { rep_p->nocopy_p = True; return *this; } private: //# Data // Pool of data blocks static ObjectPool, uInt> theirPool; // Mutex for thread-safe access to theirPool. static Mutex theirMutex; // Value representation AutoDiffRep *rep_p; //# Methods // Release a struct of value and derivative data void release() { if (!rep_p->nocopy_p) { ScopedMutexLock locker(theirMutex); theirPool.release(rep_p, rep_p->nd_p); } else { rep_p->nocopy_p = False; } } }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-2.4.1/scimath/Mathematics/AutoDiff.tcc000066400000000000000000000150531321422335000213600ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef SCIMATH_AUTODIFF_TCC #define SCIMATH_AUTODIFF_TCC //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Statics template ObjectPool, uInt> AutoDiff::theirPool; template Mutex AutoDiff::theirMutex; template AutoDiff::AutoDiff() : rep_p(0) { ScopedMutexLock locker(theirMutex); rep_p = theirPool.get(0); } template AutoDiff::AutoDiff(const T &v) : rep_p(0) { { ScopedMutexLock locker(theirMutex); rep_p = theirPool.get(0); } rep_p->val_p = v; } template AutoDiff::AutoDiff(const T &v, const uInt ndiffs, const uInt n) : rep_p(0) { { ScopedMutexLock locker(theirMutex); rep_p = theirPool.get(ndiffs); } rep_p->val_p = v; rep_p->grad_p = T(0); rep_p->grad_p[n] = T(1); } template AutoDiff::AutoDiff(const T &v, const uInt ndiffs) : rep_p(0) { { ScopedMutexLock locker(theirMutex); rep_p = theirPool.get(ndiffs); } rep_p->val_p = v; rep_p->grad_p = T(0); } template AutoDiff::AutoDiff(const AutoDiff &other) : rep_p(0) { if (other.rep_p->nocopy_p) { rep_p = other.rep_p; } else { { ScopedMutexLock locker(theirMutex); rep_p = theirPool.get(other.rep_p->nd_p); } rep_p->val_p = other.rep_p->val_p; rep_p->grad_p = other.rep_p->grad_p; } } template AutoDiff::AutoDiff(const T &v, const Vector &derivs) : rep_p(0) { { ScopedMutexLock locker(theirMutex); rep_p = theirPool.get(derivs.nelements()); } rep_p->val_p = v; rep_p->grad_p = derivs; } template AutoDiff::~AutoDiff() { release(); } template AutoDiff &AutoDiff::operator=(const T &v) { if (rep_p->nd_p != 0) { release(); { ScopedMutexLock locker(theirMutex); rep_p = theirPool.get(0); } } rep_p->val_p = v; return *this; } template AutoDiff &AutoDiff::operator=(const AutoDiff &other) { if (this != &other) { release(); { ScopedMutexLock locker(theirMutex); rep_p = theirPool.get(other.rep_p->nd_p); } rep_p->val_p = other.rep_p->val_p; rep_p->grad_p = other.rep_p->grad_p; } return *this; } template void AutoDiff::operator*=(const AutoDiff &other) { if (other.rep_p->nd_p != 0) { if (rep_p->nd_p == 0) { T v = rep_p->val_p; release(); { ScopedMutexLock locker(theirMutex); rep_p = theirPool.get(other.rep_p->nd_p); } rep_p->grad_p = other.rep_p->grad_p; rep_p->grad_p *= v; rep_p->val_p = v; } else { for (uInt i=0; ind_p ; i++) { rep_p->grad_p[i] = rep_p->val_p*other.rep_p->grad_p[i] + other.rep_p->val_p*rep_p->grad_p[i]; } } } else { for (uInt i=0; ind_p ; i++) rep_p->grad_p[i] *= other.rep_p->val_p; } rep_p->val_p *= other.rep_p->val_p; } template void AutoDiff::operator/=(const AutoDiff &other) { T temp = other.rep_p->val_p * other.rep_p->val_p; if (other.rep_p->nd_p != 0) { if (rep_p->nd_p == 0) { T v = rep_p->val_p; release(); { ScopedMutexLock locker(theirMutex); rep_p = theirPool.get(other.rep_p->nd_p); } rep_p->grad_p = other.rep_p->grad_p; rep_p->grad_p *= (-v/temp); rep_p->val_p = other.rep_p->val_p; } else { for (uInt i=0; ind_p ; i++) { rep_p->grad_p[i] = rep_p->grad_p[i]/other.rep_p->val_p - rep_p->val_p*(other.rep_p->grad_p[i])/temp; } } } else { rep_p->grad_p /= other.rep_p->val_p; } rep_p->val_p /= other.rep_p->val_p; } template void AutoDiff::operator+=(const AutoDiff &other) { if (other.rep_p->nd_p != 0) { if (rep_p->nd_p == 0) { T v = rep_p->val_p; release(); { ScopedMutexLock locker(theirMutex); rep_p = theirPool.get(other.rep_p->nd_p); } rep_p->grad_p = other.rep_p->grad_p; rep_p->val_p = v; } else { rep_p->grad_p += other.rep_p->grad_p; } } rep_p->val_p += other.rep_p->val_p; } template void AutoDiff::operator-=(const AutoDiff &other) { if (other.rep_p->nd_p != 0) { if (rep_p->nd_p == 0) { T v = rep_p->val_p; release(); { ScopedMutexLock locker(theirMutex); rep_p = theirPool.get(other.rep_p->nd_p); } rep_p->grad_p = -other.rep_p->grad_p; rep_p->val_p = v; } else { rep_p->grad_p -= other.rep_p->grad_p; } } rep_p->val_p -= other.rep_p->val_p; } template void AutoDiff::operator*=(const T other) { rep_p->grad_p *= other; rep_p->val_p *= other; } template void AutoDiff::operator/=(const T other) { rep_p->grad_p /= other; rep_p->val_p /= other; } template void AutoDiff::operator+=(const T other) { rep_p->val_p += other; } template void AutoDiff::operator-=(const T other) { rep_p->val_p -= other; } template Vector AutoDiff::derivatives() const { return rep_p->grad_p; } template void AutoDiff::derivatives(Vector &res) const { res.resize(rep_p->nd_p); res = rep_p->grad_p; } } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/scimath/Mathematics/AutoDiffA.h000066400000000000000000000123361321422335000211400ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #ifndef SCIMATH_AUTODIFFA_H #define SCIMATH_AUTODIFFA_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward declarations template class Vector; // // 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-2.4.1/scimath/Mathematics/AutoDiffIO.h000066400000000000000000000043101321422335000212600ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Mathematics/AutoDiffIO.tcc000066400000000000000000000033751321422335000216140ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Mathematics/AutoDiffMath.h000066400000000000000000000221221321422335000216430ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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-2.4.1/scimath/Mathematics/AutoDiffMath.tcc000066400000000000000000000410501321422335000221660ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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.ref(); } template AutoDiff operator-(const AutoDiff &other) { AutoDiff tmp(other); tmp *= T(-1); return tmp.ref(); } // Binary arithmetic operators template AutoDiff operator+(const AutoDiff &left, const AutoDiff &right) { if (left.theRep()->nd_p == 0) return (left.theRep()->val_p + right); AutoDiff tmp(left); tmp += right; return tmp.ref(); } template AutoDiff operator-(const AutoDiff &left, const AutoDiff &right) { if (left.theRep()->nd_p == 0) return (left.theRep()->val_p - right); AutoDiff tmp(left); tmp -= right; return tmp.ref(); } template AutoDiff operator*(const AutoDiff &left, const AutoDiff &right) { if (left.theRep()->nd_p == 0) return (left.theRep()->val_p * right); AutoDiff tmp(left); tmp *= right; return tmp.ref(); } template AutoDiff operator/(const AutoDiff &left, const AutoDiff &right) { if (left.theRep()->nd_p == 0) return (left.theRep()->val_p / right); AutoDiff tmp(left); tmp /= right; return tmp.ref(); } template AutoDiff operator+(const AutoDiff &left, const T &right) { AutoDiff tmp(left); tmp += right; return tmp.ref(); } template AutoDiff operator-(const AutoDiff &left, const T &right) { AutoDiff tmp(left); tmp -= right; return tmp.ref(); } template AutoDiff operator* (const AutoDiff &left, const T &right) { AutoDiff tmp(left); tmp *= right; return tmp.ref(); } template AutoDiff operator/(const AutoDiff &left, const T &right) { AutoDiff tmp(left); tmp /= right; return tmp.ref(); } template AutoDiff operator+(const T &left, const AutoDiff &right) { AutoDiff tmp(right); tmp += left; return tmp.ref(); } template AutoDiff operator-(const T &left, const AutoDiff &right) { AutoDiff tmp(right); tmp *= T(-1); tmp += left; return tmp.ref(); } template AutoDiff operator*(const T &left, const AutoDiff &right) { AutoDiff tmp(right); tmp *= left; return tmp.ref(); } template AutoDiff operator/(const T &left, const AutoDiff &right) { AutoDiff tmp(right); T tv(right.theRep()->val_p); tmp.theRep()->val_p = left/tv; tmp.theRep()->grad_p *= -tmp.theRep()->val_p/tv; return tmp.ref(); } template AutoDiff acos(const AutoDiff &ad) { AutoDiff tmp(ad); T tv = tmp.theRep()->val_p; tmp.theRep()->grad_p /= T(-sqrt(T(1) - tv*tv)); tmp.theRep()->val_p = acos(tv); return tmp.ref(); } template AutoDiff asin(const AutoDiff &ad) { AutoDiff tmp(ad); T tv = tmp.theRep()->val_p; tmp.theRep()->grad_p /= T(sqrt(T(1) - tv*tv)); tmp.theRep()->val_p = asin(tv); return tmp.ref(); } template AutoDiff atan(const AutoDiff &ad) { AutoDiff tmp(ad); T tv = tmp.theRep()->val_p; tmp.theRep()->grad_p /= T(1) + tv*tv; tmp.theRep()->val_p = atan(tv); return tmp.ref(); } 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.theRep()->val_p = atan2(y.theRep()->val_p, x.theRep()->val_p); return tmp.ref(); } template AutoDiff cos(const AutoDiff &ad) { AutoDiff tmp(ad); T tv = tmp.theRep()->val_p; tmp.theRep()->grad_p *= T(-sin(tv)); tmp.theRep()->val_p = cos(tv); return tmp.ref(); } template AutoDiff cosh(const AutoDiff &ad) { AutoDiff tmp(ad); T tv = tmp.theRep()->val_p; tmp.theRep()->grad_p *= T(sinh(tv)); tmp.theRep()->val_p = cosh(tv); return tmp.ref(); } template AutoDiff sin(const AutoDiff &ad) { AutoDiff tmp(ad); T tv = tmp.theRep()->val_p; tmp.theRep()->grad_p *= T(cos(tv)); tmp.theRep()->val_p = sin(tv); return tmp.ref(); } template AutoDiff sinh(const AutoDiff &ad) { AutoDiff tmp(ad); T tv = tmp.theRep()->val_p; tmp.theRep()->grad_p *= T(cosh(tv)); tmp.theRep()->val_p = sinh(tv); return tmp.ref(); } template AutoDiff exp(const AutoDiff &ad) { AutoDiff tmp(ad); tmp.theRep()->val_p = exp(ad.theRep()->val_p); tmp.theRep()->grad_p *= tmp.theRep()->val_p; return tmp.ref(); } template AutoDiff log(const AutoDiff &ad) { AutoDiff tmp(ad); T tv = tmp.theRep()->val_p; tmp.theRep()->grad_p /= tv; tmp.theRep()->val_p = log(tv); return tmp.ref(); } template AutoDiff log10(const AutoDiff &ad) { static const T l10 = T(log(10.0)); AutoDiff tmp(ad); T tv = tmp.theRep()->val_p; tmp.theRep()->grad_p /= tv*l10; tmp.theRep()->val_p = log10(tv); return tmp.ref(); } template AutoDiff erf(const AutoDiff &ad) { AutoDiff tmp(ad); T tv = tmp.theRep()->val_p; tmp.theRep()->grad_p *= T(T(C::_2_sqrtpi)*exp(-tv*tv)); tmp.theRep()->val_p = erf(tv); return tmp.ref(); } template AutoDiff erfc(const AutoDiff &ad) { AutoDiff tmp(ad); T tv = tmp.theRep()->val_p; tmp.theRep()->grad_p *= T(T(-C::_2_sqrtpi)*exp(-tv*tv)); tmp.theRep()->val_p = erfc(tv); return tmp.ref(); } template AutoDiff pow(const AutoDiff &a, const AutoDiff &b) { if (b.theRep()->nd_p == 0) return pow(a, b.theRep()->val_p); T ta = a.theRep()->val_p; T tb = b.theRep()->val_p; T value = pow(ta, tb); T temp2 = tb * pow(ta, tb - T(1)); AutoDiff tmp(b); tmp.theRep()->grad_p *= value * T(log(ta)); for (uInt i=0; ind_p; i++) { tmp.theRep()->grad_p[i] += a.theRep()->grad_p[i]*temp2; } tmp.theRep()->val_p = value; return tmp.ref(); } template AutoDiff pow(const AutoDiff &a, const T &b) { AutoDiff tmp(a); T ta = a.theRep()->val_p; tmp.theRep()->grad_p *= b*pow(ta, b-T(1)); tmp.theRep()->val_p = pow(ta, b); return tmp.ref(); } template AutoDiff square(const AutoDiff &ad) { AutoDiff tmp(ad); tmp.theRep()->val_p = square(tmp.theRep()->val_p); tmp.theRep()->grad_p *= T(2)*tmp.theRep()->val_p; return tmp.ref(); } template AutoDiff cube(const AutoDiff &ad) { AutoDiff tmp(ad); tmp.theRep()->val_p = cube(tmp.theRep()->val_p); tmp.theRep()->grad_p *= T(3)*square(tmp.theRep()->val_p); return tmp.ref(); } template AutoDiff sqrt(const AutoDiff &ad) { AutoDiff tmp(ad); tmp.theRep()->val_p = sqrt(tmp.theRep()->val_p); tmp.theRep()->grad_p /= T(2)*tmp.theRep()->val_p; return tmp.ref(); } template AutoDiff tan(const AutoDiff &ad) { AutoDiff tmp(ad); T tv = tmp.theRep()->val_p; T temp = cos(tv); temp *= temp; tmp.theRep()->grad_p /= temp; tmp.theRep()->val_p = tan(tv); return tmp.ref(); } template AutoDiff tanh(const AutoDiff &ad) { AutoDiff tmp(ad); T tv = tmp.theRep()->val_p; T temp = cosh(tv); temp *= temp; tmp.theRep()->grad_p /= temp; tmp.theRep()->val_p = tanh(tv); return tmp.ref(); } 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.theRep()->val_p is positive, F is positive in // the small neighborhood. AutoDiff tmp(ad); if (ad.theRep()->val_p < T(0)) tmp *= T(-1); return tmp.ref(); } 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.theRep()->val_p = fmod(x.theRep()->val_p, c); return tmp.ref(); } template AutoDiff fmod(const AutoDiff &x, const AutoDiff &c) { AutoDiff tmp(x); tmp.theRep()->val_p = fmod(x.theRep()->val_p, c.theRep()->val_p); return tmp.ref(); } template AutoDiff floor(const AutoDiff &ad) { AutoDiff tmp(ad); tmp.theRep()->val_p = floor(ad.theRep()->val_p); tmp.theRep()->grad_p = T(0); return tmp.ref(); } template AutoDiff ceil(const AutoDiff &ad) { AutoDiff tmp(ad); tmp.theRep()->val_p = ceil(ad.theRep()->val_p); tmp.theRep()->grad_p = T(0); return tmp.ref(); } template Bool operator>(const AutoDiff &left, const AutoDiff &right) { return (left.theRep()->val_p > right.theRep()->val_p); } template Bool operator<(const AutoDiff &left, const AutoDiff &right) { return (left.theRep()->val_p < right.theRep()->val_p); } template Bool operator>=(const AutoDiff &left, const AutoDiff &right) { return (left.theRep()->val_p >= right.theRep()->val_p); } template Bool operator<=(const AutoDiff &left, const AutoDiff &right) { return (left.theRep()->val_p <= right.theRep()->val_p); } template Bool operator==(const AutoDiff &left, const AutoDiff &right) { return (left.theRep()->val_p == right.theRep()->val_p); } template Bool operator!=(const AutoDiff &left, const AutoDiff &right) { return (left.theRep()->val_p != right.theRep()->val_p); } // Compare an AutoDiff and a constant template Bool operator>(const AutoDiff &left,const T &right) { return (left.theRep()->val_p > right); } template Bool operator<(const AutoDiff &left,const T &right) { return (left.theRep()->val_p < right); } template Bool operator>=(const AutoDiff &left,const T &right) { return (left.theRep()->val_p >= right); } template Bool operator<=(const AutoDiff &left,const T &right) { return (left.theRep()->val_p <= right); } template Bool operator==(const AutoDiff &left,const T &right) { return (left.theRep()->val_p == right); } template Bool operator!=(const AutoDiff &left,const T &right) { return (left.theRep()->val_p != right); } // Compare a constant and an AutoDiff template Bool operator>(const T &left, const AutoDiff &right) { return (left > right.theRep()->val_p); } template Bool operator<(const T &left, const AutoDiff &right) { return (left < right.theRep()->val_p); } template Bool operator>=(const T &left, const AutoDiff &right) { return (left >= right.theRep()->val_p); } template Bool operator<=(const T &left, const AutoDiff &right) { return (left <= right.theRep()->val_p); } template Bool operator==(const T &left, const AutoDiff &right) { return (left == right.theRep()->val_p); } template Bool operator!=(const T &left, const AutoDiff &right) { return (left != right.theRep()->val_p); } // Near comparisons template Bool near(const AutoDiff &left, const AutoDiff &right) { return (near(left.theRep()->val_p, right.theRep()->val_p)); } template Bool near(const T &left, const AutoDiff &right) { return near(left, right.theRep()->val_p); } template Bool near(const AutoDiff &left, const T &right) { return near(left.theRep()->val_p, right); } template Bool near(const AutoDiff &left, const AutoDiff &right, const Double tol) { return near(left.theRep()->val_p, right.theRep()->val_p, tol); } template Bool near(const T &left, const AutoDiff &right, const Double tol) { return near(left, right.theRep()->val_p, tol); } template Bool near(const AutoDiff &left, const T &right, const Double tol) { return near(left.theRep()->val_p, right, tol); } template Bool allnear(const AutoDiff &left, const AutoDiff &right) { return (near(left.theRep()->val_p, right.theRep()->val_p)); } template Bool allnear(const T &left, const AutoDiff &right) { return near(left, right.theRep()->val_p); } template Bool allnear(const AutoDiff &left, const T &right) { return near(left.theRep()->val_p, right); } template Bool allnear(const AutoDiff &left, const AutoDiff &right, const Double tol) { return near(left.theRep()->val_p, right.theRep()->val_p, tol); } template Bool allnear(const T &left, const AutoDiff &right, const Double tol) { return near(left, right.theRep()->val_p, tol); } template Bool allnear(const AutoDiff &left, const T &right, const Double tol) { return near(left.theRep()->val_p, right, tol); } template Bool nearAbs(const AutoDiff &left, const AutoDiff &right) { return (nearAbs(left.theRep()->val_p, right.theRep()->val_p)); } template Bool nearAbs(const T &left, const AutoDiff &right) { return nearAbs(left, right.theRep()->val_p); } template Bool nearAbs(const AutoDiff &left, const T &right) { return nearAbs(left.theRep()->val_p, right); } template Bool nearAbs(const AutoDiff &left, const AutoDiff &right, const Double tol) { return nearAbs(left.theRep()->val_p, right.theRep()->val_p, tol); } template Bool nearAbs(const T &left, const AutoDiff &right, const Double tol) { return nearAbs(left, right.theRep()->val_p, tol); } template Bool nearAbs(const AutoDiff &left, const T &right, const Double tol) { return nearAbs(left.theRep()->val_p, right, tol); } template Bool allnearAbs(const AutoDiff &left, const AutoDiff &right) { return (nearAbs(left.theRep()->val_p, right.theRep()->val_p)); } template Bool allnearAbs(const T &left, const AutoDiff &right) { return nearAbs(left, right.theRep()->val_p); } template Bool allnearAbs(const AutoDiff &left, const T &right) { return nearAbs(left.theRep()->val_p, right); } template Bool allnearAbs(const AutoDiff &left, const AutoDiff &right, const Double tol) { return nearAbs(left.theRep()->val_p, right.theRep()->val_p, tol); } template Bool allnearAbs(const T &left, const AutoDiff &right, const Double tol) { return nearAbs(left, right.theRep()->val_p, tol); } template Bool allnearAbs(const AutoDiff &left, const T &right, const Double tol) { return nearAbs(left.theRep()->val_p, right, tol); } // Test special values template Bool isNaN (const AutoDiff &val) { return isNaN(val.theRep()->val_p); } template Bool isInf(AutoDiff &val) { return isInf(val.theRep()->val_p); } template AutoDiff min(const AutoDiff &left, const AutoDiff &right) { AutoDiff tmp = (left.theRep()->val_p <= right.theRep()->val_p) ? left : right; return tmp.ref(); } template AutoDiff max(const AutoDiff &left, const AutoDiff &right) { AutoDiff tmp = (left.theRep()->val_p <= right.theRep()->val_p) ? right : left; return tmp.ref(); } } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/scimath/Mathematics/AutoDiffRep.h000066400000000000000000000112751321422335000215070ustar00rootroot00000000000000//# AutoDiffRep.h: Representation of an automatic differential class data //# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #ifndef SCIMATH_AUTODIFFREP_H #define SCIMATH_AUTODIFFREP_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward declarations // // Representation of an automatic differential class data // // // // // // // // //
      • AutoDiff // // // // Class that represents partial derivatives obtained by automatic // differentiation. // // // // Structure (only a class since cxx2html cannot handle struct) representing // the data necessary for automatic differentiation. The structure contains a // value, and the derivatives of the value with respect to the number of // dependend variables. // // The actual differentiation and access is done through the // AutoDiff class. // // // See the example in AutoDiff // // // // 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 off // template class AutoDiffRep { 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. AutoDiffRep(); // Construct a constant with a value of v. Zero derivatives. explicit AutoDiffRep(const T &v); // Given a function f(x0,x1,...,xn,...). Construct with // a total number of derivatives ndiffs. The nth derivative is one, and all // others are zero. The value v is the value of xn. AutoDiffRep(const T &v, const uInt ndiffs, const uInt n); // Given a function f(x0,x1,...,xn,...). Construct with // a total number of derivatives ndiffs, and a value of xn. // All derivatives are zero. AutoDiffRep(const T &v, const uInt ndiffs); // Construct with ndiffs derivatives. All values and derivatives zero AutoDiffRep(const uInt ndiffs); // Construct one from another (deep copy) AutoDiffRep(const AutoDiffRep &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, ... AutoDiffRep(const T &v, const Vector &derivs); // Destructor ~AutoDiffRep(); //# Operators // Assign a constant to variable. All derivatives // are zero. AutoDiffRep &operator=(const T &v); // Assign one to another (deep copy). AutoDiffRep &operator=(const AutoDiffRep &other); //# Member functions //# Data // The function value T val_p; // The number of derivatives uInt nd_p; // A flag indicating that value will not be used anymore (to stop // superfluous copying) Bool nocopy_p; // The derivatives Vector grad_p; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-2.4.1/scimath/Mathematics/AutoDiffRep.tcc000066400000000000000000000057271321422335000220360ustar00rootroot00000000000000//# AutoDiffRep.cc: Representation of an automatic differential class data //# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef SCIMATH_AUTODIFFREP_TCC #define SCIMATH_AUTODIFFREP_TCC //# Includes #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Constructors template AutoDiffRep::AutoDiffRep() : val_p(T(0.0)), nd_p(0), nocopy_p(False), grad_p(0) {} template AutoDiffRep::AutoDiffRep(const T &v) : val_p(v), nd_p(0), nocopy_p(False), grad_p(0) {} template AutoDiffRep::AutoDiffRep(const T &v, const uInt ndiffs, const uInt n) : val_p(v), nd_p(ndiffs), nocopy_p(False), grad_p(ndiffs) { grad_p = T(0); grad_p[n] = T(1); } template AutoDiffRep::AutoDiffRep(const T &v, const uInt ndiffs) : val_p(v), nd_p(ndiffs), nocopy_p(False), grad_p(ndiffs) { grad_p = T(0); } template AutoDiffRep::AutoDiffRep(const uInt ndiffs) : val_p(0), nd_p(ndiffs), nocopy_p(False), grad_p(ndiffs) { grad_p = T(0); } template AutoDiffRep::AutoDiffRep(const AutoDiffRep &other) : val_p(other.val_p), nd_p(other.nd_p), nocopy_p(False), grad_p(other.nd_p) { grad_p = other.grad_p; } template AutoDiffRep::AutoDiffRep(const T &v, const Vector &derivs) : val_p(v), nd_p(derivs.nelements()), nocopy_p(False), grad_p(nd_p) { grad_p = derivs; } template AutoDiffRep::~AutoDiffRep() {} template AutoDiffRep &AutoDiffRep::operator=(const T &v) { val_p = v; nd_p = 0; grad_p.resize(nd_p); return *this; } template AutoDiffRep &AutoDiffRep::operator=(const AutoDiffRep &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; } } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/scimath/Mathematics/AutoDiffX.h000066400000000000000000000123311321422335000211620ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #ifndef SCIMATH_AUTODIFFX_H #define SCIMATH_AUTODIFFX_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward declarations template class Vector; // // 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-2.4.1/scimath/Mathematics/ChauvenetCriterionStatistics.h000066400000000000000000000077331321422335000252170ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: Array.h 21545 2015-01-22 19:36:35Z gervandiepen $ #ifndef SCIMATH_CHAUVENETCRITERIONSTATISTICS_H #define SCIMATH_CHAUVENETCRITERIONSTATISTICS_H #include #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. template class ChauvenetCriterionStatistics : public ConstrainedRangeStatistics { public: // 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); 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; Int _maxIterations; Bool _rangeIsSet; uInt _niter; void _setRange(); }; } #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-2.4.1/scimath/Mathematics/ChauvenetCriterionStatistics.tcc000066400000000000000000000076611321422335000255410ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: Array.h 21545 2015-01-22 19:36:35Z gervandiepen $ #ifndef SCIMATH_CHAUVENETCRITERIONSTATISTICS_TCC #define SCIMATH_CHAUVENETCRITERIONSTATISTICS_TCC #include #include #include #include namespace casacore { CASA_STATD ChauvenetCriterionStatistics::ChauvenetCriterionStatistics( Double zscore, Int maxIterations ) : ConstrainedRangeStatistics(), _zscore(zscore), _maxIterations(maxIterations), _rangeIsSet(False), _niter(0) {} CASA_STATD ChauvenetCriterionStatistics::~ChauvenetCriterionStatistics() {} CASA_STATD ChauvenetCriterionStatistics& ChauvenetCriterionStatistics::operator=( const ChauvenetCriterionStatistics& other ) { if (this == &other) { return *this; } ClassicalStatistics::operator=(other); _zscore = other._zscore; _maxIterations = other._maxIterations; _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; uInt 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); CountedPtr > range = new std::pair( 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-2.4.1/scimath/Mathematics/ClassicalStatistics.h000066400000000000000000001072511321422335000233100ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: Array.h 21545 2015-01-22 19:36:35Z gervandiepen $ #ifndef SCIMATH_CLASSICALSTATS_H #define SCIMATH_CLASSICALSTATS_H #include #include #include #include #include #include #include namespace casacore { template class PtrHolder; // 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 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 (eg median) calculations are desired. // I attempted to write this class using the Composite design pattern, with eg the // _unweightedStats() and _weightedStats() methods in their own class, but for reasons I // don't understand, that impacted performance significantly. So I'm using the current // architecture, which I know is a bit a maintenance nightmare. template class ClassicalStatistics : public StatisticsAlgorithm { 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( CountedPtr knownNpts=NULL, CountedPtr knownMin=NULL, CountedPtr knownMax=NULL, uInt binningThreshholdSizeBytes=4096*4096, Bool persistSortedArray=False, uInt64 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, CountedPtr knownNpts=NULL, CountedPtr knownMin=NULL, CountedPtr knownMax=NULL, uInt binningThreshholdSizeBytes=4096*4096, Bool persistSortedArray=False, uInt64 nBins=10000 ); // get the median of the absolute deviation about the median of the data. virtual AccumType getMedianAbsDevMed( CountedPtr knownNpts=NULL, CountedPtr knownMin=NULL, CountedPtr knownMax=NULL, uInt binningThreshholdSizeBytes=4096*4096, Bool persistSortedArray=False, uInt64 nBins=10000 ); // Get the specified quantiles. fractions must be between 0 and 1, // noninclusive. virtual std::map getQuantiles( const std::set& fractions, CountedPtr knownNpts=NULL, CountedPtr knownMin=NULL, CountedPtr knownMax=NULL, uInt binningThreshholdSizeBytes=4096*4096, Bool persistSortedArray=False, uInt64 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. 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 std::pair getStatisticIndex(StatisticsData::STATS stat); // Has any data been added to this object? Will return False if the object has // been reset and no data have been added afterward. Bool hasData() const { return _hasData; } // 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. void setDataProvider(StatsDataProvider *dataProvider); void setStatsToCalculate(std::set& stats); 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. inline virtual void _accumNpts( uInt64& npts, const DataIterator& dataBegin, Int64 nr, uInt dataStride ) const; virtual void _accumNpts( uInt64& npts, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) const; virtual void _accumNpts( uInt64& npts, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) const; virtual void _accumNpts( uInt64& npts, const DataIterator& dataBegin, Int64 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, Int64 nr, uInt dataStride ) const; virtual void _accumNpts( uInt64& npts, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) const; virtual void _accumNpts( uInt64& npts, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 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, Int64 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(); // scan dataset(s) to find min and max void _doMinMax(AccumType& vmin, AccumType& vmax); // // 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 binDesc. // Each array within binCounts will have the same number of elements as the // number of bins in its corresponding histogram in binDesc. virtual void _findBins( vector >& binCounts, vector >& sameVal, vector& allSame, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const vector::BinDesc>& binDesc, const vector& maxLimit ) const; virtual void _findBins( vector >& binCounts, vector >& sameVal, vector& allSame, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude, const vector::BinDesc>& binDesc, const vector& maxLimit ) const; virtual void _findBins( vector >& binCounts, vector >& sameVal, vector& allSame, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const vector::BinDesc>& binDesc, const vector& maxLimit ) const; virtual void _findBins( vector >& binCounts, vector >& sameVal, vector& allSame, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude, const vector::BinDesc>& binDesc, const vector& maxLimit ) const; virtual void _findBins( vector >& binCounts, vector >& sameVal, vector& allSame, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride, const vector::BinDesc>& binDesc, const vector& maxLimit ) const ; virtual void _findBins( vector >& binCounts, vector >& sameVal, vector& allSame, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude, const vector::BinDesc>& binDesc, const vector& maxLimit ) const; virtual void _findBins( vector >& binCounts, vector >& sameVal, vector& allSame, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude, const vector::BinDesc>& binDesc, const vector& maxLimit ) const; virtual void _findBins( vector >& binCounts, vector >& sameVal, vector& allSame, const DataIterator& dataBegin, const WeightsIterator& weightBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const vector::BinDesc>& binDesc, const vector& maxLimit ) const; // Bool _getDoMaxMin() const { return _doMaxMin; } Int64 _getIDataset() const { return _idataset; } virtual StatsData _getInitialStats() const; AccumType _getStatistic(StatisticsData::STATS stat); StatsData _getStatistics(); // retreive stats structure. Allows derived classes to maintain their own // StatsData structs. inline virtual StatsData& _getStatsData() { return _statsData; } inline virtual const StatsData& _getStatsData() const { return _statsData; } // virtual void _minMax( CountedPtr& mymin, CountedPtr& mymax, const DataIterator& dataBegin, Int64 nr, uInt dataStride ) const; virtual void _minMax( CountedPtr& mymin, CountedPtr& mymax, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) const; virtual void _minMax( CountedPtr& mymin, CountedPtr& mymax, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) const; virtual void _minMax( CountedPtr& mymin, CountedPtr& mymax, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) const; virtual void _minMax( CountedPtr& mymin, CountedPtr& mymax, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride ) const; virtual void _minMax( CountedPtr& mymin, CountedPtr& mymax, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) const; virtual void _minMax( CountedPtr& mymin, CountedPtr& mymax, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) const; virtual void _minMax( CountedPtr& mymin, CountedPtr& mymax, const DataIterator& dataBegin, const WeightsIterator& weightBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) const; // // // populate an unsorted array with valid data. // no weights, no mask, no ranges virtual void _populateArray( vector& ary, const DataIterator& dataBegin, Int64 nr, uInt dataStride ) const; // ranges virtual void _populateArray( vector& ary, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) const; virtual void _populateArray( vector& ary, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) const; // mask and ranges virtual void _populateArray( vector& ary, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) const; // weights virtual void _populateArray( vector& ary, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride ) const; // weights and ranges virtual void _populateArray( vector& ary, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) const; // weights and mask virtual void _populateArray( vector& ary, const DataIterator& dataBegin, const WeightsIterator& weightBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) const; // weights, mask, ranges virtual void _populateArray( vector& ary, const DataIterator& dataBegin, const WeightsIterator& weightBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) const; // // // Create a 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( vector >& arys, uInt64& currentCount, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const vector > &includeLimits, uInt64 maxCount ) const; // ranges virtual void _populateArrays( vector >& arys, uInt64& currentCount, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude, const vector > &includeLimits, uInt64 maxCount ) const; virtual void _populateArrays( vector >& arys, uInt64& currentCount, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const vector > &includeLimits, uInt64 maxCount ) const; // mask and ranges virtual void _populateArrays( vector >& arys, uInt64& currentCount, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude, const vector > &includeLimits, uInt64 maxCount ) const; // weights virtual void _populateArrays( vector >& arys, uInt64& currentCount, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride, const vector > &includeLimits, uInt64 maxCount ) const; // weights and ranges virtual void _populateArrays( vector >& arys, uInt64& currentCount, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude, const vector > &includeLimits, uInt64 maxCount ) const; // weights and mask virtual void _populateArrays( vector >& arys, uInt64& currentCount, const DataIterator& dataBegin, const WeightsIterator& weightBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const vector > &includeLimits, uInt64 maxCount ) const; // weights, mask, ranges virtual void _populateArrays( vector >& arys, uInt64& currentCount, const DataIterator& dataBegin, const WeightsIterator& weightBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude, const vector > &includeLimits, uInt64 maxCount ) const; // // // no weights, no mask, no ranges virtual Bool _populateTestArray( vector& ary, const DataIterator& dataBegin, Int64 nr, uInt dataStride, uInt maxElements ) const; // ranges virtual Bool _populateTestArray( vector& ary, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude, uInt maxElements ) const; // mask virtual Bool _populateTestArray( vector& ary, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, uInt maxElements ) const; // mask and ranges virtual Bool _populateTestArray( vector& ary, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude, uInt maxElements ) const; // weights virtual Bool _populateTestArray( vector& ary, const DataIterator& dataBegin, const WeightsIterator& weightBegin, Int64 nr, uInt dataStride, uInt maxElements ) const; // weights and ranges virtual Bool _populateTestArray( vector& ary, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude, uInt maxElements ) const; // weights and mask virtual Bool _populateTestArray( vector& ary, const DataIterator& dataBegin, const WeightsIterator& weightBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, uInt maxElements ) const; // weights, mask, ranges virtual Bool _populateTestArray( vector& ary, const DataIterator& dataBegin, const WeightsIterator& weightBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude, uInt maxElements ) const; // // // no weights, no mask, no ranges virtual void _unweightedStats( StatsData& stats, uInt64& ngood, LocationType& location, const DataIterator& dataBegin, Int64 nr, uInt dataStride ); // no weights, no mask virtual void _unweightedStats( StatsData& stats, uInt64& ngood, LocationType& location, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ); virtual void _unweightedStats( StatsData& stats, uInt64& ngood, LocationType& location, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ); virtual void _unweightedStats( StatsData& stats, uInt64& ngood, LocationType& location, const DataIterator& dataBegin, Int64 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, Int64 nr, uInt dataStride ); virtual void _weightedStats( StatsData& stats, LocationType& location, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ); virtual void _weightedStats( StatsData& stats, LocationType& location, const DataIterator& dataBegin, const WeightsIterator& weightBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ); virtual void _weightedStats( StatsData& stats, LocationType& location, const DataIterator& dataBegin, const WeightsIterator& weightBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ); // private: StatsData _statsData; Int64 _idataset; Bool _calculateAsAdded, _doMaxMin, _doMedAbsDevMed, _mustAccumulate, _hasData; // mutables, used to mitigate repeated code mutable typename vector::const_iterator _dend, _diter; mutable vector::const_iterator _citer; mutable vector::const_iterator _dsiter; mutable std::map _masks; mutable uInt _maskStride; mutable std::map _weights; mutable std::map _ranges; mutable std::map _isIncludeRanges; mutable Bool _hasMask, _hasRanges, _hasWeights, _myIsInclude; mutable DataRanges _myRanges; mutable MaskIterator _myMask; mutable DataIterator _myData; mutable WeightsIterator _myWeights; mutable uInt _dataCount, _myStride; mutable uInt64 _myCount; // tally the number of data points that fall into each bin provided by binDesc // Any points that are less than binDesc.minLimit or greater than // binDesc.minLimit + binDesc.nBins*binDesc.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. vector > _binCounts( vector >& sameVal, const vector::BinDesc>& binDesc ); void _computeBins( vector >& bins, vector >& sameVal, vector& allSame, DataIterator dataIter, MaskIterator maskIter, WeightsIterator weightsIter, uInt64 count, const vector::BinDesc>& binDesc, const vector& maxLimit ); void _computeDataArray( vector& ary, DataIterator dataIter, MaskIterator maskIter, WeightsIterator weightsIter, uInt64 dataCount ); void _computeDataArrays( vector >& arys, uInt64& currentCount, DataIterator dataIter, MaskIterator maskIter, WeightsIterator weightsIter, uInt64 dataCount, const vector >& includeLimits, uInt64 maxCount ); void _computeMinMax( CountedPtr& mymax, CountedPtr& mymin, DataIterator dataIter, MaskIterator maskIter, WeightsIterator weightsIter, uInt64 dataCount ); void _computeStats( StatsData& stats, uInt64& ngood, LocationType& location, DataIterator dataIter, MaskIterator maskIter, WeightsIterator weightsIter, uInt64 count ); // convert in place by taking the absolute value of the difference of the vector and the median static void _convertToAbsDevMedArray(vector& myArray, AccumType median); // 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( vector& array ); void _createDataArrays( vector >& arrays, const vector > &includeLimits, uInt64 maxCount ); // extract data from multiple histograms given by binDesc. // 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 repsective histograms. The ordering of the maps in the returned vector represent // the ordering of histograms in binDesc. binDesc should contain // non-overlapping histograms and the histograms should be specified in ascending order. vector > _dataFromMultipleBins( const vector::BinDesc>& binDesc, uInt64 maxArraySize, const vector >& dataIndices, uInt64 nBins ); vector > _dataFromSingleBins( const vector& binNpts, uInt64 maxArraySize, const vector >& binLimits, const vector >& dataIndices, uInt64 nBins ); Int64 _doNpts(); // increment the relevant loop counters Bool _increment(Bool includeIDataset); // increment thread-based iterators void _incrementThreadIters( DataIterator& dataIter, MaskIterator& maskIter, WeightsIterator& weightsIter, uInt64& offset, uInt nthreads ) const; // get the values for the specified indices in the sorted array of all good data std::map _indicesToValues( CountedPtr knownNpts, CountedPtr knownMin, CountedPtr knownMax, uInt64 maxArraySize, const std::set& dataIndices, Bool persistSortedArray, uInt64 nBins ); void _initIterators(); void _initLoopVars(); void _initThreadVars( uInt& nBlocks, uInt64& extra, uInt& nthreads, PtrHolder& dataIter, PtrHolder& maskIter, PtrHolder& weightsIter, PtrHolder& offset, uInt nThreadsMax ) const; // Determine by scanning the dataset if the number of good points is smaller than // maxArraySize. If so, arrayToSort will contain the unsorted // data values. If not, this vector will be empty. Bool _isNptsSmallerThan(vector& arrayToSort, uInt maxArraySize); // If allowPad is True, then pad the lower side of the lowest bin and the // higher side of the highest bin so that minData and maxData do not fall on the edge // of their respective bins. If false, no padding so that minData and maxData are also // exactly the histogram abscissa limits. static void _makeBins( typename StatisticsUtilities::BinDesc& bins, AccumType minData, AccumType maxData, uInt maxBins, Bool allowPad ); static void _mergeResults( vector >& bins, vector >& sameVal, vector& allSame, const PtrHolder > >& tBins, const PtrHolder > >& tSameVal, const PtrHolder >& tAllSame, uInt nThreadsMax ); // get the index (for odd npts) or indices (for even npts) of the median of the sorted array. // If knownNpts is not null, it will be used and must be correct. If it is null, the value of // _npts will be used if it has been previously calculated. If not, the data sets will // be scanned to determine npts. std::set _medianIndices(CountedPtr knownNpts); uInt _nThreadsMax() const; uInt _threadIdx() 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. Bool _valuesFromSortedArray( std::map& values, CountedPtr knownNpts, const std::set& indices, uInt64 maxArraySize, Bool persistSortedArray ); }; } #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-2.4.1/scimath/Mathematics/ClassicalStatistics.tcc000066400000000000000000004035311321422335000236320ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: Array.h 21545 2015-01-22 19:36:35Z gervandiepen $ #ifndef SCIMATH_CLASSICALSTATISTICS_TCC #define SCIMATH_CLASSICALSTATISTICS_TCC #include #include #include #include #include #include #ifdef _OPENMP #include #endif namespace casacore { // min > max indicates that these quantities have not be calculated CASA_STATD ClassicalStatistics::ClassicalStatistics() : StatisticsAlgorithm(), _statsData(initializeStatsData()), _idataset(0), _calculateAsAdded(False), _doMaxMin(True), _doMedAbsDevMed(False), _mustAccumulate(False) { reset(); } CASA_STATD ClassicalStatistics::~ClassicalStatistics() {} CASA_STATD ClassicalStatistics::ClassicalStatistics( const ClassicalStatistics& cs ) : StatisticsAlgorithm(cs), _statsData(cs._statsData), _idataset(cs._idataset),_calculateAsAdded(cs._calculateAsAdded), _doMaxMin(cs._doMaxMin), _doMedAbsDevMed(cs._doMedAbsDevMed), _mustAccumulate(cs._mustAccumulate), _hasData(cs._hasData) { } CASA_STATD ClassicalStatistics& ClassicalStatistics::operator=( const ClassicalStatistics& other ) { if (this == &other) { return *this; } StatisticsAlgorithm::operator=(other); _statsData = copy(_statsData); _idataset = other._idataset; _calculateAsAdded = other._calculateAsAdded; _doMaxMin = other._doMaxMin; _doMedAbsDevMed = other._doMedAbsDevMed; _mustAccumulate = other._mustAccumulate; _hasData = other._hasData; return *this; } CASA_STATD StatisticsAlgorithm* ClassicalStatistics::clone() const { return new ClassicalStatistics(*this); } CASA_STATD AccumType ClassicalStatistics::getMedian( CountedPtr knownNpts, CountedPtr knownMin, CountedPtr knownMax, uInt binningThreshholdSizeBytes, Bool persistSortedArray, uInt64 nBins ) { if (! _getStatsData().median.null()) { return *_getStatsData().median; } std::set indices = _medianIndices(knownNpts); std::map indexToValue = _indicesToValues( knownNpts, knownMin, knownMax, binningThreshholdSizeBytes/sizeof(AccumType), indices, persistSortedArray, nBins ); _getStatsData().median = indexToValue.size() == 1 ? new AccumType(indexToValue[*indices.begin()]) : new AccumType( ( indexToValue[*indices.begin()] + indexToValue[*indices.rbegin()] )/AccumType(2) ); return *_getStatsData().median; } CASA_STATD std::set ClassicalStatistics::_medianIndices( CountedPtr knownNpts ) { std::set indices; uInt64 mynpts = knownNpts.null() ? getNPts() : *knownNpts; if (mynpts % 2 == 0) { indices.insert(mynpts/2 - 1); indices.insert(mynpts/2); } else { indices.insert(mynpts/2); } return indices; } CASA_STATD AccumType ClassicalStatistics::getMedianAbsDevMed( CountedPtr knownNpts, CountedPtr knownMin, CountedPtr knownMax, uInt binningThreshholdSizeBytes, Bool persistSortedArray, uInt64 nBins ) { if (! _getStatsData().medAbsDevMed.null()) { return *_getStatsData().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( knownNpts, knownMin, knownMax, binningThreshholdSizeBytes, persistSortedArray, nBins ); std::set indices = _medianIndices(knownNpts); // throw the proper switch _doMedAbsDevMed = True; std::map indexToValue = _indicesToValues( knownNpts, knownMin, knownMax, binningThreshholdSizeBytes/sizeof(AccumType), indices, persistSortedArray, nBins ); _doMedAbsDevMed = False; _getStatsData().medAbsDevMed = indexToValue.size() == 1 ? new AccumType(indexToValue[*indices.begin()]) : new AccumType( ( indexToValue[*indices.begin()] + indexToValue[*indices.rbegin()] )/AccumType(2) ); return *_getStatsData().medAbsDevMed; } CASA_STATD AccumType ClassicalStatistics::getMedianAndQuantiles( std::map& quantiles, const std::set& fractions, CountedPtr knownNpts, CountedPtr knownMin, CountedPtr knownMax, uInt binningThreshholdSizeBytes, Bool persistSortedArray, uInt64 nBins ) { std::set medianIndices; quantiles.clear(); CountedPtr mynpts = knownNpts.null() ? new uInt64(getNPts()) : knownNpts; ThrowIf( *mynpts == 0, "No valid data found" ); if (_getStatsData().median.null()) { medianIndices = _medianIndices(mynpts); } std::map quantileToIndex = StatisticsData::indicesFromFractions( *mynpts, fractions ); std::set indices = medianIndices; std::map::const_iterator qToIIter = quantileToIndex.begin(); std::map::const_iterator qToIEnd = quantileToIndex.end(); while(qToIIter != qToIEnd) { indices.insert(qToIIter->second); ++qToIIter; } std::map indexToValue = _indicesToValues( mynpts, knownMin, knownMax, binningThreshholdSizeBytes/sizeof(AccumType), indices, persistSortedArray, nBins ); if (_getStatsData().median.null()) { _getStatsData().median = *mynpts % 2 == 0 ? new AccumType( ( indexToValue[*medianIndices.begin()] + indexToValue[*medianIndices.rbegin()] )/AccumType(2) ) : new AccumType(indexToValue[*medianIndices.begin()]); } std::set::const_iterator fIter = fractions.begin(); std::set::const_iterator fEnd = fractions.end(); while (fIter != fEnd) { quantiles[*fIter] = indexToValue[quantileToIndex[*fIter]]; ++fIter; } return *_getStatsData().median; } CASA_STATD void ClassicalStatistics::getMinMax( AccumType& mymin, AccumType& mymax ) { if ( _getStatsData().min.null() || _getStatsData().max.null()) { 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 = new AccumType(mymin); _getStatsData().max = new AccumType(mymax); return; } mymin = *_getStatsData().min; mymax = *_getStatsData().max; } 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, CountedPtr knownNpts, CountedPtr knownMin, CountedPtr knownMax, uInt binningThreshholdSizeBytes, Bool persistSortedArray, uInt64 nBins ) { if (fractions.empty()) { return std::map(); } 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" ); ThrowIf( *fractions.begin() <= 0 || *fractions.rbegin() >= 1, "Value of all quantiles must be between 0 and 1 (noninclusive)" ); uInt64 mynpts = knownNpts.null() ? getNPts() : *knownNpts; ThrowIf(mynpts == 0, "No valid data found"); std::map quantileToIndexMap = 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::map quantileToValue; std::set uniqueIndices; std::map::const_iterator qToIIter = quantileToIndexMap.begin(); std::map::const_iterator qToIEnd = quantileToIndexMap.end(); while(qToIIter != qToIEnd) { uniqueIndices.insert(qToIIter->second); ++qToIIter; } std::map indexToValue = _indicesToValues( knownNpts, knownMin, knownMax, binningThreshholdSizeBytes/sizeof(AccumType), uniqueIndices, persistSortedArray, nBins ); qToIIter = quantileToIndexMap.begin(); while (qToIIter != qToIEnd) { Double quantile = qToIIter->first; uInt64 index = qToIIter->second; quantileToValue[quantile] = indexToValue[index]; ++qToIIter; } return quantileToValue; } CASA_STATD void ClassicalStatistics::setCalculateAsAdded( Bool c ) { ThrowIf ( this->_getDataProvider() && c, "Logic Error: It is nonsensical to call " + String(__func__) + " method " "with a True value if one is using a data provider" ); ThrowIf( _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); _hasData = True; } CASA_STATD void ClassicalStatistics::setStatsToCalculate( std::set& stats ) { ThrowIf( _calculateAsAdded && _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() { this->_setSortedArray(vector()); _getStatsData().median = NULL; _mustAccumulate = True; _hasData = True; if (_calculateAsAdded) { _getStatistics(); StatisticsAlgorithm::reset(); } } CASA_STATD void ClassicalStatistics::reset() { _clearStats(); StatisticsAlgorithm::reset(); _hasData = False; } CASA_STATD void ClassicalStatistics::_clearStats() { _statsData = initializeStatsData(); _idataset = 0; _doMedAbsDevMed = False; _mustAccumulate = True; } CASA_STATD std::pair 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 StatsData 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; Record r = toRecord(_getStatistics()); String 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); } _initIterators(); uInt nThreadsMax = _nThreadsMax(); PtrHolder > tStats( new StatsData[ ClassicalStatisticsData::CACHE_PADDING*nThreadsMax ], True ); for (uInt i=0; i dataIter; PtrHolder maskIter; PtrHolder weightsIter; PtrHolder offset; _initThreadVars( nBlocks, extra, nthreads, dataIter, maskIter, weightsIter, offset, nThreadsMax ); if (_hasWeights) { stats.weighted = True; } if (_hasMask) { stats.masked = True; } #ifdef _OPENMP #pragma omp parallel for num_threads(nthreads) #endif for (uInt i=0; i > 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); } StatsData 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::_incrementThreadIters( DataIterator& dataIter, MaskIterator& maskIter, WeightsIterator& weightsIter, uInt64& offset, uInt nthreads ) const { uInt increment = nthreads*ClassicalStatisticsData::BLOCK_SIZE*_myStride; if (offset+increment >= _myCount*_myStride) { // necessary because in some cases std::advance will segfault // if advanced past the end of the data structure return; } std::advance(dataIter, increment); if (_hasWeights) { std::advance(weightsIter, increment); } if (_hasMask) { std::advance(maskIter, nthreads*ClassicalStatisticsData::BLOCK_SIZE*_maskStride); } offset += increment; } CASA_STATD void ClassicalStatistics::_computeStats( StatsData& stats, uInt64& ngood, LocationType& location, DataIterator dataIter, MaskIterator maskIter, WeightsIterator weightsIter, uInt64 count ) { if (_hasWeights) { stats.weighted = True; if (_hasMask) { stats.masked = True; if (_hasRanges) { _weightedStats( stats, location, dataIter, weightsIter, count, _myStride, maskIter, _maskStride, _myRanges, _myIsInclude ); } else { _weightedStats( stats, location, dataIter, weightsIter, count, _myStride, maskIter, _maskStride ); } } else if (_hasRanges) { _weightedStats( stats, location, dataIter, weightsIter, count, _myStride,_myRanges, _myIsInclude ); } else { // has weights, but no mask nor ranges _weightedStats( stats, location, dataIter, weightsIter, count, _myStride ); } } else if (_hasMask) { // this data set has no weights, but does have a mask stats.masked = True; if (_hasRanges) { _unweightedStats( stats, ngood, location, dataIter, count, _myStride, maskIter, _maskStride, _myRanges, _myIsInclude ); } else { _unweightedStats( stats, ngood, location, dataIter, count, _myStride, maskIter, _maskStride ); } } else if (_hasRanges) { // this data set has no weights no mask, but does have a set of ranges // associated with it _unweightedStats( stats, ngood, location, dataIter, count, _myStride, _myRanges, _myIsInclude ); } 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, _myStride ); } if (! _hasWeights) { stats.sumweights += ngood; } } CASA_STATD Bool ClassicalStatistics::_increment(Bool includeIDataset) { if (includeIDataset) { ++_idataset; } StatsDataProvider *dataProvider = this->_getDataProvider(); 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 ClassicalStatistics::_accumNpts( uInt64& npts, const DataIterator& /*dataBegin*/, Int64 nr, uInt /*dataStride*/ ) const { npts += nr; } CASA_STATD void ClassicalStatistics::_accumNpts( uInt64& npts, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) const { DataIterator datum = dataBegin; Int64 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 ) ) { ++npts; } StatisticsIncrementer::increment( datum, count, dataStride ); } } CASA_STATD void ClassicalStatistics::_accumNpts( uInt64& npts, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) const { DataIterator datum = dataBegin; MaskIterator mask = maskBegin; Int64 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, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) const { DataIterator datum = dataBegin; MaskIterator mask = maskBegin; Int64 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 ) ) { ++npts; } StatisticsIncrementer::increment( datum, count, mask, dataStride, maskStride ); } } CASA_STATD void ClassicalStatistics::_accumNpts( uInt64& npts, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride ) const { DataIterator datum = dataBegin; WeightsIterator weight = weightsBegin; Int64 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, Int64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) const { DataIterator datum = dataBegin; WeightsIterator weight = weightsBegin; Int64 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 ) ) { ++npts; } StatisticsIncrementer::increment( datum, count, weight, dataStride ); } } CASA_STATD void ClassicalStatistics::_accumNpts( uInt64& npts, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) const { DataIterator datum = dataBegin; WeightsIterator weight = weightsBegin; MaskIterator mask = maskBegin; Int64 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 ) ) { ++npts; } StatisticsIncrementer::increment( datum, count, weight, mask, dataStride, maskStride ); } } CASA_STATD void ClassicalStatistics::_accumNpts( uInt64& npts, const DataIterator& dataBegin, const WeightsIterator& weightBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) const { DataIterator datum = dataBegin; WeightsIterator weight = weightBegin; MaskIterator mask = maskBegin; Int64 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, weight, datum ); } } CASA_STATD uInt ClassicalStatistics::_nThreadsMax() const { #ifdef _OPENMP if (omp_get_num_threads() > 1) { // we are being called from an already parallized block of code, // so we should not parallelize return 1; } // we are being called from a single threaded block of code, // so parallelize const StatsDataProvider *dataProvider = this->_getDataProvider(); if (dataProvider) { uInt n = dataProvider->getNMaxThreads(); if (n > 0) { return n; } } return omp_get_max_threads(); #else return 1; #endif } CASA_STATD uInt ClassicalStatistics::_threadIdx() const { #ifdef _OPENMP uInt tid = omp_get_thread_num(); #else uInt tid = 0; #endif return tid * ClassicalStatisticsData::CACHE_PADDING; } CASA_STATD vector > ClassicalStatistics::_binCounts( vector >& sameVal, const vector::BinDesc>& binDesc ) { typename vector::BinDesc>::const_iterator bDesc = binDesc.begin(); typename vector::BinDesc>::const_iterator iDesc = bDesc; typename vector::BinDesc>::const_iterator eDesc = binDesc.end(); if (binDesc.size() > 1) { // sanity check typename StatisticsUtilities::BinDesc prevDesc; while (iDesc != eDesc) { if (iDesc != bDesc) { ThrowIf ( iDesc->minLimit <= prevDesc.minLimit, "Logic Error: histograms are not monotonically increasing" ); } prevDesc = *iDesc; ++iDesc; } } std::vector allSame(binDesc.size(), True); vector > bins(binDesc.size()); iDesc = bDesc; vector >::iterator bBins = bins.begin(); vector >::iterator iBins = bBins; vector >::iterator eBins = bins.end(); while (iBins != eBins) { *iBins = vector(iDesc->nBins, 0); ++iDesc; ++iBins; } sameVal = vector >(binDesc.size(), NULL); vector maxLimit(binDesc.size()); typename vector::iterator bMaxLimit = maxLimit.begin(); typename vector::iterator iMaxLimit = bMaxLimit; typename vector::iterator eMaxLimit = maxLimit.end(); iDesc = bDesc; while(iMaxLimit != eMaxLimit) { *iMaxLimit = iDesc->minLimit + (AccumType)(iDesc->nBins)*(iDesc->binWidth); ++iMaxLimit; ++iDesc; } _initIterators(); const uInt nThreadsMax = _nThreadsMax(); PtrHolder > > tBins( new std::vector >[ ClassicalStatisticsData::CACHE_PADDING*nThreadsMax ], True ); PtrHolder > > tSameVal( new std::vector >[ ClassicalStatisticsData::CACHE_PADDING*nThreadsMax ], True ); PtrHolder > tAllSame( new std::vector[ ClassicalStatisticsData::CACHE_PADDING*nThreadsMax ], True ); for (uInt tid=0; tid dataIter; PtrHolder maskIter; PtrHolder weightsIter; PtrHolder offset; _initThreadVars( nBlocks, extra, nthreads, dataIter, maskIter, weightsIter, offset, nThreadsMax ); #ifdef _OPENMP #pragma omp parallel for num_threads(nthreads) #endif for (uInt i=0; i::_mergeResults( vector >& bins, vector >& sameVal, vector& allSame, const PtrHolder > >& tBins, const PtrHolder > >& tSameVal, const PtrHolder >& tAllSame, uInt nThreadsMax ) { // merge results from individual threads (tBins, tSameVal, tAllSame) // into single data structures (bins, sameVal, allSame) for (uInt tid=0; tid >::iterator iter; vector >::iterator end = bins.end(); typename vector >::iterator siter; typename vector >::iterator send = sameVal.end(); vector::iterator aiter; uInt idx8 = ClassicalStatisticsData::CACHE_PADDING*tid; vector >::const_iterator titer = tBins[idx8].begin(); for (iter=bins.begin(); iter!=end; ++iter, ++titer) { std::transform( iter->begin(), iter->end(), titer->begin(), iter->begin(), std::plus() ); } typename vector >::const_iterator viter = tSameVal[idx8].begin(); vector::const_iterator witer = tAllSame[idx8].begin(); for ( siter=sameVal.begin(), aiter=allSame.begin(); siter!=send; ++siter, ++viter, ++aiter, ++witer ) { if (! *aiter) { // won't have the same values, do nothing } if (*witer && *aiter) { if ( viter->null() || (! siter->null() && *(*siter) == *(*viter)) ) { // no unflagged values in this chunk or both // have the all the same values, do nothing } else if (siter->null()) { siter->reset(new AccumType(*(*viter))); } else { // both are not null, and they do not have // the same values siter->reset(); *aiter = False; } } else { // *aiter = True, *witer = False, all values are not the same siter->reset(); *aiter = False; } } } } CASA_STATD void ClassicalStatistics::_computeBins( vector >& bins, vector >& sameVal, vector& allSame, DataIterator dataIter, MaskIterator maskIter, WeightsIterator weightsIter, uInt64 count, const vector::BinDesc>& binDesc, const vector& maxLimit ) { if (_hasWeights) { if (_hasMask) { if (_hasRanges) { _findBins( bins, sameVal, allSame, dataIter, weightsIter, count, _myStride, maskIter, _maskStride, _myRanges, _myIsInclude, binDesc, maxLimit ); } else { _findBins( bins, sameVal, allSame, dataIter, weightsIter, count, _myStride, maskIter, _maskStride, binDesc, maxLimit ); } } else if (_hasRanges) { _findBins( bins, sameVal, allSame, dataIter, weightsIter, count, _myStride, _myRanges, _myIsInclude, binDesc, maxLimit ); } else { // has weights, but no mask nor ranges _findBins( bins, sameVal, allSame, dataIter, weightsIter, count, _myStride, binDesc, maxLimit ); } } else if (_hasMask) { // this data set has no weights, but does have a mask if (_hasRanges) { _findBins( bins, sameVal, allSame, dataIter, count, _myStride, maskIter, _maskStride, _myRanges, _myIsInclude, binDesc, maxLimit ); } else { _findBins( bins, sameVal, allSame, dataIter, count, _myStride, maskIter, _maskStride, binDesc, maxLimit ); } } else if (_hasRanges) { // this data set has no weights no mask, but does have a set of ranges // associated with it _findBins( bins, sameVal, allSame, dataIter, count, _myStride, _myRanges, _myIsInclude, binDesc, 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, _myStride, binDesc, maxLimit ); } } CASA_STATD void ClassicalStatistics::_createDataArray( std::vector& ary ) { _initIterators(); const uInt nThreadsMax = _nThreadsMax(); PtrHolder > tAry( new std::vector[ ClassicalStatisticsData::CACHE_PADDING*nThreadsMax ], True ); while (True) { _initLoopVars(); uInt nBlocks, nthreads; uInt64 extra; PtrHolder dataIter; PtrHolder maskIter; PtrHolder weightsIter; PtrHolder offset; _initThreadVars( nBlocks, extra, nthreads, dataIter, maskIter, weightsIter, offset, nThreadsMax ); #ifdef _OPENMP #pragma omp parallel for num_threads(nthreads) #endif for (uInt i=0; i& v = tAry[ClassicalStatisticsData::CACHE_PADDING*tid]; ary.insert(ary.end(), v.begin(), v.end()); } } CASA_STATD void ClassicalStatistics::_computeDataArray( vector& ary, DataIterator dataIter, MaskIterator maskIter, WeightsIterator weightsIter, uInt64 dataCount ) { if (_hasWeights) { if (_hasMask) { if (_hasRanges) { _populateArray( ary, dataIter, weightsIter, dataCount, _myStride, maskIter, _maskStride, _myRanges, _myIsInclude ); } else { _populateArray( ary, dataIter, weightsIter, dataCount, _myStride, maskIter, _maskStride ); } } else if (_hasRanges) { _populateArray( ary, dataIter, weightsIter, dataCount, _myStride, _myRanges, _myIsInclude ); } else { // has weights, but no mask nor ranges _populateArray( ary, dataIter, weightsIter, dataCount, _myStride ); } } else if (_hasMask) { // this data set has no weights, but does have a mask if (_hasRanges) { _populateArray( ary, dataIter, dataCount, _myStride, maskIter, _maskStride, _myRanges, _myIsInclude ); } else { _populateArray( ary, dataIter, dataCount, _myStride, maskIter, _maskStride ); } } else if (_hasRanges) { // this data set has no weights no mask, but does have a set of ranges // associated with it _populateArray( ary, dataIter, dataCount, _myStride, _myRanges, _myIsInclude ); } 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, _myStride ); } } CASA_STATD void ClassicalStatistics::_createDataArrays( std::vector >& arys, const std::vector >& includeLimits, uInt64 maxCount ) { typename std::vector >::const_iterator bLimits = includeLimits.begin(); typename std::vector >::const_iterator iLimits = bLimits; typename std::vector >::const_iterator eLimits = includeLimits.end(); std::pair prevLimits; while(iLimits != eLimits) { // sanity checks if (iLimits->first >= iLimits->second) { ostringstream os; os << "Logic Error: bin limits are nonsensical: " << *iLimits; ThrowCc(os.str()); } if (iLimits != bLimits) { if ( iLimits->first <= prevLimits.first || iLimits->second <= prevLimits.second ) { ostringstream os; os << "Logic Error: bin limits are not in order: " << prevLimits << " , " << *iLimits; ThrowCc(os.str()); } } prevLimits = *iLimits; ++iLimits; } _initIterators(); const uInt nThreadsMax = _nThreadsMax(); PtrHolder > > tArys( new std::vector >[ ClassicalStatisticsData::CACHE_PADDING*nThreadsMax ], True ); PtrHolder tCurrentCount( new uInt64[ ClassicalStatisticsData::CACHE_PADDING*nThreadsMax ], True ); for (uInt tid=0; tid dataIter; PtrHolder maskIter; PtrHolder weightsIter; PtrHolder offset; _initThreadVars( nBlocks, extra, nthreads, dataIter, maskIter, weightsIter, offset, nThreadsMax ); for (uInt tid=0; tid >::iterator iter = arys.begin(); typename vector >::iterator end = arys.end(); typename vector >::const_iterator titer = tArys[idx8].begin(); for (; iter!=end; ++iter, ++titer) { iter->insert(iter->end(), titer->begin(), titer->end()); } } } CASA_STATD void ClassicalStatistics::_computeDataArrays( vector >& arys, uInt64& currentCount, DataIterator dataIter, MaskIterator maskIter, WeightsIterator weightsIter, uInt64 dataCount, const vector >& includeLimits, uInt64 maxCount ) { if (_hasWeights) { if (_hasMask) { if (_hasRanges) { _populateArrays( arys, currentCount, dataIter, weightsIter, dataCount, _myStride, maskIter, _maskStride, _myRanges, _myIsInclude, includeLimits, maxCount ); } else { _populateArrays( arys, currentCount, dataIter, weightsIter, dataCount, _myStride, maskIter, _maskStride, includeLimits, maxCount ); } } else if (_hasRanges) { _populateArrays( arys, currentCount, dataIter, weightsIter, dataCount, _myStride, _myRanges, _myIsInclude, includeLimits, maxCount ); } else { // has weights, but no mask nor ranges _populateArrays( arys, currentCount, dataIter, weightsIter, dataCount, _myStride, includeLimits, maxCount ); } } else if (_hasMask) { // this data set has no weights, but does have a mask if (_hasRanges) { _populateArrays( arys, currentCount, dataIter, dataCount, _myStride, maskIter, _maskStride, _myRanges, _myIsInclude, includeLimits, maxCount ); } else { _populateArrays( arys, currentCount, dataIter, dataCount, _myStride, maskIter, _maskStride, includeLimits, maxCount ); } } else if (_hasRanges) { // this data set has no weights no mask, but does have a set of ranges // associated with it _populateArrays( arys, currentCount, dataIter, dataCount, _myStride, _myRanges, _myIsInclude, 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, _myStride, includeLimits, maxCount ); } } CASA_STATD vector > ClassicalStatistics::_dataFromMultipleBins( const vector::BinDesc>& binDesc, uInt64 maxArraySize, const vector >& dataIndices, uInt64 nBins ) { // dataIndices are relative to minimum bin minimum border vector > sameVal(binDesc.size(), NULL); vector > binCounts = _binCounts(sameVal, binDesc); vector >::const_iterator bIdxSet = dataIndices.begin(); vector >::const_iterator iIdxSet = bIdxSet; vector >::const_iterator eIdxSet = dataIndices.end(); typename vector >::const_iterator bSameVal = sameVal.begin(); typename vector >::const_iterator iSameVal = bSameVal; vector >::const_iterator bCountSet = binCounts.begin(); vector >::const_iterator iCountSet = bCountSet; typename vector::BinDesc>::const_iterator bDesc = binDesc.begin(); typename vector::BinDesc>::const_iterator iDesc = bDesc; typename vector::BinDesc>::const_iterator eDesc = binDesc.end(); std::map > histToIdxValMap; vector vnpts; vector > vlimits; vector > vindices; 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; while (iIdxSet != eIdxSet) { std::set::const_iterator iIdx = iIdxSet->begin(); std::set::const_iterator eIdx = iIdxSet->end(); if (iSameVal->null()) { // values in this histogram are not all the same vector::const_iterator bCounts = iCountSet->begin(); vector::const_iterator iCounts = bCounts; vector::const_iterator eCounts = iCountSet->end(); uInt64 dataCount = 0; uInt64 prevDataCount = 0; uInt64 loopCount = 0; 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 std::pair binLimits; binLimits.first = iDesc->minLimit + (AccumType)loopCount*(iDesc->binWidth); binLimits.second = binLimits.first + iDesc->binWidth; std::set 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(binLimits); // 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[binLimits.first] = iDesc->minLimit; vindices.push_back(newDataIndices); } prevDataCount = dataCount; ++iCounts; ++loopCount; } } else { // values in this histogram are all the same std::map mymap; while (iIdx != eIdx) { mymap[*iIdx] = *(*iSameVal); ++iIdx; } histToIdxValMap[iDesc->minLimit] = mymap; } ++iIdxSet; ++iSameVal; ++iCountSet; ++iDesc; } if (! vnpts.empty()) { vector > dataFromBins = _dataFromSingleBins( vnpts, maxArraySize, vlimits, vindices, nBins ); typename vector >::const_iterator iDataSet = dataFromBins.begin(); typename vector >::const_iterator eDataSet = dataFromBins.end(); vector >::iterator iNewToOld = vNewToOld.begin(); typename vector >::const_iterator iVLimits = vlimits.begin(); while(iDataSet != eDataSet) { AccumType myHistKey = binToHistogramMap[iVLimits->first]; std::map mymap; typename std::map::const_iterator iData = iDataSet->begin(); typename std::map::const_iterator eData = iDataSet->end(); while(iData != eData) { uInt64 newIdx = iData->first; uInt64 oldIdx = (*iNewToOld)[newIdx]; mymap[oldIdx] = iData->second; ++iData; } histToIdxValMap[myHistKey].insert(mymap.begin(), mymap.end()); ++iNewToOld; ++iDataSet; ++iVLimits; } } vector > ret; iDesc = bDesc; while (iDesc != eDesc) { ret.push_back(histToIdxValMap[iDesc->minLimit]); ++iDesc; } return ret; } CASA_STATD vector > ClassicalStatistics::_dataFromSingleBins( const vector& binNpts, uInt64 maxArraySize, const vector >& binLimits, const vector >& dataIndices, uInt64 nBins ) { uInt64 totalPts = std::accumulate(binNpts.begin(), binNpts.end(), 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 vector > dataArrays(binLimits.size(), vector(0)); _createDataArrays(dataArrays, binLimits, totalPts); vector::const_iterator bNpts = binNpts.begin(); vector::const_iterator iNpts = bNpts; typename vector >::iterator bArrays = dataArrays.begin(); typename vector >::iterator iArrays = bArrays; typename vector >::iterator eArrays = dataArrays.end(); while (iArrays != eArrays) { ThrowIf( iArrays->size() != *iNpts, "Logic Error: data array has " + String::toString(iArrays->size()) + " elements but it should have " + String::toString(*iNpts) + ". Please file a bug report and include your dataset and your inputs" ); ++iArrays; ++iNpts; } std::vector >::const_iterator bIdxSet = dataIndices.begin(); std::vector >::const_iterator iIdxSet = bIdxSet; std::vector >::const_iterator eIdxSet = dataIndices.end(); iNpts = bNpts; vector > ret(binLimits.size()); typename vector >::iterator bRet = ret.begin(); typename vector >::iterator iRet = bRet; // typename vector >::iterator eRet = ret.end(); iArrays = bArrays; while(iIdxSet != eIdxSet) { std::set::const_iterator initer = iIdxSet->begin(); std::set::const_iterator inend = iIdxSet->end(); uInt prevIdx = 0; while (initer != inend) { ThrowIf( *initer >= *iNpts, "Logic Error: aryIdx " + String::toString(*initer) + " 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" ); (*iRet)[*initer] = GenSort::kthLargest( &((*iArrays)[prevIdx]), *iNpts - prevIdx, *initer - prevIdx ); prevIdx = *initer; ++initer; } ++iIdxSet; ++iNpts; ++iArrays; ++iRet; } return ret; } else { // bin contents are too large to be sorted in memory, this bin must be sub-binned typename vector >::const_iterator bLimits = binLimits.begin(); typename vector >::const_iterator iLimits = bLimits; typename vector >::const_iterator eLimits = binLimits.end(); vector::BinDesc> binDesc; while (iLimits != eLimits) { // we want at least 1000 bins nBins = max(nBins, (uInt64)1000); typename StatisticsUtilities::BinDesc histogram; _makeBins( histogram, iLimits->first, iLimits->second, nBins, False ); binDesc.push_back(histogram); ++iLimits; } return _dataFromMultipleBins(binDesc, maxArraySize, dataIndices, nBins); } } CASA_STATD void ClassicalStatistics::_convertToAbsDevMedArray( vector& myArray, AccumType median ) { typename vector::iterator iter = myArray.begin(); typename vector::iterator end = myArray.end(); while (iter != end) { *iter = abs(*iter - median); ++iter; } } CASA_STATD void ClassicalStatistics::_doMinMax( AccumType& datamin, AccumType& datamax ) { _initIterators(); const uInt nThreadsMax = _nThreadsMax(); PtrHolder > tmin( new CountedPtr[ ClassicalStatisticsData::CACHE_PADDING*nThreadsMax ], True ); PtrHolder > tmax( new CountedPtr[ ClassicalStatisticsData::CACHE_PADDING*nThreadsMax ], True ); while (True) { _initLoopVars(); uInt nBlocks, nthreads; uInt64 extra; PtrHolder dataIter; PtrHolder maskIter; PtrHolder weightsIter; PtrHolder offset; _initThreadVars( nBlocks, extra, nthreads, dataIter, maskIter, weightsIter, offset, nThreadsMax ); #ifdef _OPENMP #pragma omp parallel for num_threads(nthreads) #endif for (uInt i=0; i mymax; CountedPtr mymin; for (uInt i=0; i *mymax) { mymax = tmax[idx8]; } } } ThrowIf ( mymax.null() || mymin.null(), "No valid data found" ); datamin = *mymin; datamax = *mymax; } CASA_STATD void ClassicalStatistics::_computeMinMax( CountedPtr& mymax, CountedPtr& mymin, DataIterator dataIter, MaskIterator maskIter, WeightsIterator weightsIter, uInt64 dataCount ) { if (_hasWeights) { if (_hasMask) { if (_hasRanges) { _minMax( mymin, mymax, dataIter, weightsIter, dataCount, _myStride, maskIter, _maskStride, _myRanges, _myIsInclude ); } else { _minMax( mymin, mymax, dataIter, weightsIter, dataCount, _myStride, maskIter, _maskStride ); } } else if (_hasRanges) { _minMax( mymin, mymax, dataIter, weightsIter, dataCount, _myStride, _myRanges, _myIsInclude ); } else { // has weights, but no mask nor ranges _minMax( mymin, mymax, dataIter, weightsIter, dataCount, _myStride ); } } else if (_hasMask) { // this data set has no weights, but does have a mask if (_hasRanges) { _minMax( mymin, mymax, dataIter, dataCount, _myStride, maskIter, _maskStride, _myRanges, _myIsInclude ); } else { _minMax( mymin, mymax, dataIter, dataCount, _myStride, maskIter, _maskStride ); } } else if (_hasRanges) { // this data set has no weights no mask, but does have a set of ranges // associated with it _minMax( mymin, mymax, dataIter, dataCount, _myStride, _myRanges, _myIsInclude ); } 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, _myStride); } } CASA_STATD Int64 ClassicalStatistics::_doNpts() { _initIterators(); uInt64 npts = 0; while (True) { _initLoopVars(); if (_hasWeights) { if (_hasMask) { if (_hasRanges) { _accumNpts( npts, _myData, _myWeights, _myCount, _myStride, _myMask, _maskStride, _myRanges, _myIsInclude ); } else { _accumNpts( npts, _myData, _myWeights, _myCount, _myStride, _myMask, _maskStride ); } } else if (_hasRanges) { _accumNpts( npts, _myData, _myWeights, _myCount, _myStride, _myRanges, _myIsInclude ); } else { // has weights, but no mask nor ranges _accumNpts( npts, _myData, _myWeights, _myCount, _myStride ); } } else if (_hasMask) { // this data set has no weights, but does have a mask if (_hasRanges) { _accumNpts( npts, _myData, _myCount, _myStride, _myMask, _maskStride, _myRanges, _myIsInclude ); } else { _accumNpts( npts, _myData, _myCount, _myStride, _myMask, _maskStride ); } } else if (_hasRanges) { // this data set has no weights no mask, but does have a set of ranges // associated with it _accumNpts( npts, _myData, _myCount, _myStride, _myRanges, _myIsInclude ); } else { // simplest case, this data set has no weights, no mask, nor any ranges associated // with it. _accumNpts( npts, _myData, _myCount, _myStride ); } if (_increment(False)) { break; } } ThrowIf (npts == 0, "No valid data found"); return npts; } // 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 - *_statsData.median) : *datum; \ if (myDatum >= bBinDesc->minLimit && myDatum < *maxLimit.rbegin()) { \ iCounts = bCounts; \ iSameVal = bSameVal; \ iAllSame = bAllSame; \ iBinDesc = bBinDesc; \ iMaxLimit = bMaxLimit; \ while (iBinDesc != eBinDesc) { \ if (myDatum >= iBinDesc->minLimit && myDatum < *iMaxLimit) { \ AccumType idx = (myDatum - iBinDesc->minLimit)/iBinDesc->binWidth; \ ++(*iCounts)[StatisticsUtilities::getInt(idx)]; \ if (*iAllSame) { \ if (iSameVal->null()) { \ *iSameVal = new AccumType(myDatum); \ } \ else { \ *iAllSame = myDatum == *(*iSameVal); \ if (! *iAllSame) { \ *iSameVal = NULL; \ } \ } \ } \ break; \ } \ ++iCounts; \ ++iSameVal; \ ++iAllSame; \ ++iBinDesc; \ ++iMaxLimit; \ } \ } CASA_STATD void ClassicalStatistics::_findBins( vector >& binCounts, vector >& sameVal, vector& allSame, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const vector::BinDesc>& binDesc, const vector& maxLimit ) const { vector >::iterator bCounts = binCounts.begin(); vector >::iterator iCounts = bCounts; typename vector >::iterator bSameVal = sameVal.begin(); typename vector >::iterator iSameVal = bSameVal; vector::iterator bAllSame = allSame.begin(); vector::iterator iAllSame = bAllSame; typename vector::BinDesc>::const_iterator bBinDesc = binDesc.begin(); typename vector::BinDesc>::const_iterator iBinDesc = bBinDesc; typename vector::BinDesc>::const_iterator eBinDesc = binDesc.end(); typename vector::const_iterator bMaxLimit = maxLimit.begin(); typename vector::const_iterator iMaxLimit = bMaxLimit; DataIterator datum = dataBegin; Int64 count = 0; while (count < nr) { _findBinCode StatisticsIncrementer::increment( datum, count, dataStride ); } } CASA_STATD void ClassicalStatistics::_findBins( vector >& binCounts, vector >& sameVal, vector& allSame, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude, const vector::BinDesc>& binDesc, const vector& maxLimit ) const { vector >::iterator bCounts = binCounts.begin(); vector >::iterator iCounts = bCounts; typename vector >::iterator bSameVal = sameVal.begin(); typename vector >::iterator iSameVal = bSameVal; vector::iterator bAllSame = allSame.begin(); vector::iterator iAllSame = bAllSame; typename vector::BinDesc>::const_iterator bBinDesc = binDesc.begin(); typename vector::BinDesc>::const_iterator iBinDesc = bBinDesc; typename vector::BinDesc>::const_iterator eBinDesc = binDesc.end(); typename vector::const_iterator bMaxLimit = maxLimit.begin(); typename vector::const_iterator iMaxLimit = bMaxLimit; DataIterator datum = dataBegin; Int64 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 ) ) { _findBinCode } StatisticsIncrementer::increment( datum, count, dataStride ); } } CASA_STATD void ClassicalStatistics::_findBins( vector >& binCounts, vector >& sameVal, vector& allSame, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const vector::BinDesc>& binDesc, const vector& maxLimit ) const { vector >::iterator bCounts = binCounts.begin(); vector >::iterator iCounts = bCounts; typename vector >::iterator bSameVal = sameVal.begin(); typename vector >::iterator iSameVal = bSameVal; vector::iterator bAllSame = allSame.begin(); vector::iterator iAllSame = bAllSame; typename vector::BinDesc>::const_iterator bBinDesc = binDesc.begin(); typename vector::BinDesc>::const_iterator iBinDesc = bBinDesc; typename vector::BinDesc>::const_iterator eBinDesc = binDesc.end(); typename vector::const_iterator bMaxLimit = maxLimit.begin(); typename vector::const_iterator iMaxLimit = bMaxLimit; DataIterator datum = dataBegin; MaskIterator mask = maskBegin; Int64 count = 0; while (count < nr) { if (*mask) { _findBinCode } StatisticsIncrementer::increment( datum, count, mask, dataStride, maskStride ); } } CASA_STATD void ClassicalStatistics::_findBins( vector >& binCounts, vector >& sameVal, vector& allSame, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude, const vector::BinDesc>& binDesc, const vector& maxLimit ) const { vector >::iterator bCounts = binCounts.begin(); vector >::iterator iCounts = bCounts; typename vector >::iterator bSameVal = sameVal.begin(); typename vector >::iterator iSameVal = bSameVal; vector::iterator bAllSame = allSame.begin(); vector::iterator iAllSame = bAllSame; typename vector::BinDesc>::const_iterator bBinDesc = binDesc.begin(); typename vector::BinDesc>::const_iterator iBinDesc = bBinDesc; typename vector::BinDesc>::const_iterator eBinDesc = binDesc.end(); typename vector::const_iterator bMaxLimit = maxLimit.begin(); typename vector::const_iterator iMaxLimit = bMaxLimit; DataIterator datum = dataBegin; MaskIterator mask = maskBegin; Int64 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 ) ) { _findBinCode } StatisticsIncrementer::increment( datum, count, mask, dataStride, maskStride ); } } CASA_STATD void ClassicalStatistics::_findBins( vector >& binCounts, vector >& sameVal, vector& allSame, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride, const vector::BinDesc>& binDesc, const vector& maxLimit ) const { vector >::iterator bCounts = binCounts.begin(); vector >::iterator iCounts = bCounts; typename vector >::iterator bSameVal = sameVal.begin(); typename vector >::iterator iSameVal = bSameVal; vector::iterator bAllSame = allSame.begin(); vector::iterator iAllSame = bAllSame; typename vector::BinDesc>::const_iterator bBinDesc = binDesc.begin(); typename vector::BinDesc>::const_iterator iBinDesc = bBinDesc; typename vector::BinDesc>::const_iterator eBinDesc = binDesc.end(); typename vector::const_iterator bMaxLimit = maxLimit.begin(); typename vector::const_iterator iMaxLimit = bMaxLimit; DataIterator datum = dataBegin; WeightsIterator weight = weightsBegin; Int64 count = 0; while (count < nr) { if (*weight > 0) { _findBinCode } StatisticsIncrementer::increment( datum, count, weight, dataStride ); } } CASA_STATD void ClassicalStatistics::_findBins( vector >& binCounts, vector >& sameVal, vector& allSame, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude, const vector::BinDesc>& binDesc, const vector& maxLimit ) const { vector >::iterator bCounts = binCounts.begin(); vector >::iterator iCounts = bCounts; typename vector >::iterator bSameVal = sameVal.begin(); typename vector >::iterator iSameVal = bSameVal; vector::iterator bAllSame = allSame.begin(); vector::iterator iAllSame = bAllSame; typename vector::BinDesc>::const_iterator bBinDesc = binDesc.begin(); typename vector::BinDesc>::const_iterator iBinDesc = bBinDesc; typename vector::BinDesc>::const_iterator eBinDesc = binDesc.end(); typename vector::const_iterator bMaxLimit = maxLimit.begin(); typename vector::const_iterator iMaxLimit = bMaxLimit; DataIterator datum = dataBegin; WeightsIterator weight = weightsBegin; Int64 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 ) ) { _findBinCode } StatisticsIncrementer::increment( datum, count, weight, dataStride ); } } CASA_STATD void ClassicalStatistics::_findBins( vector >& binCounts, vector >& sameVal, vector& allSame, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude, const vector::BinDesc>& binDesc, const vector& maxLimit ) const { vector >::iterator bCounts = binCounts.begin(); vector >::iterator iCounts = bCounts; typename vector >::iterator bSameVal = sameVal.begin(); typename vector >::iterator iSameVal = bSameVal; vector::iterator bAllSame = allSame.begin(); vector::iterator iAllSame = bAllSame; typename vector::BinDesc>::const_iterator bBinDesc = binDesc.begin(); typename vector::BinDesc>::const_iterator iBinDesc = bBinDesc; typename vector::BinDesc>::const_iterator eBinDesc = binDesc.end(); typename vector::const_iterator bMaxLimit = maxLimit.begin(); typename vector::const_iterator iMaxLimit = bMaxLimit; DataIterator datum = dataBegin; WeightsIterator weight = weightsBegin; MaskIterator mask = maskBegin; Int64 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 ) ) { _findBinCode } StatisticsIncrementer::increment( datum, count, weight, mask, dataStride, maskStride ); } } CASA_STATD void ClassicalStatistics::_findBins( vector >& binCounts, vector >& sameVal, vector& allSame, const DataIterator& dataBegin, const WeightsIterator& weightBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const vector::BinDesc>& binDesc, const vector& maxLimit ) const { vector >::iterator bCounts = binCounts.begin(); vector >::iterator iCounts = bCounts; typename vector >::iterator bSameVal = sameVal.begin(); typename vector >::iterator iSameVal = bSameVal; vector::iterator bAllSame = allSame.begin(); vector::iterator iAllSame = bAllSame; typename vector::BinDesc>::const_iterator bBinDesc = binDesc.begin(); typename vector::BinDesc>::const_iterator iBinDesc = bBinDesc; typename vector::BinDesc>::const_iterator eBinDesc = binDesc.end(); typename vector::const_iterator bMaxLimit = maxLimit.begin(); typename vector::const_iterator iMaxLimit = bMaxLimit; DataIterator datum = dataBegin; WeightsIterator weight = weightBegin; MaskIterator mask = maskBegin; Int64 count = 0; while (count < nr) { if (*mask && *weight > 0) { _findBinCode } StatisticsIncrementer::increment( datum, count, weight, mask, dataStride, maskStride ); } } CASA_STATD std::map ClassicalStatistics::_indicesToValues( CountedPtr knownNpts, CountedPtr knownMin, CountedPtr knownMax, uInt64 maxArraySize, const std::set& indices, Bool persistSortedArray, uInt64 nBins ) { std::map indexToValue; if ( _valuesFromSortedArray( indexToValue, knownNpts, indices, maxArraySize, persistSortedArray ) ) { return indexToValue; } AccumType mymin, mymax; if (knownMin.null() || knownMax.null()) { getMinMax(mymin, mymax); } else { ThrowIf( *knownMax < *knownMin, "Provided max " + String::toString(*knownMax) + " is less than provided min " + String::toString(*knownMin) ); mymin = *knownMin; mymax = *knownMax; } if (_doMedAbsDevMed) { mymax = max( abs(mymax - *_getStatsData().median), abs(mymin - *_getStatsData().median) ); mymin = AccumType(0); } if (mymax == mymin) { // data set values are all the same std::set::const_iterator iter = indices.begin(); std::set::const_iterator end = indices.end(); while(iter != end) { indexToValue[*iter] = mymin; ++iter; } return indexToValue; } vector > vindices(1, indices); AccumType pad = 1e-6*(mymax - mymin); std::pair limits(mymin - pad, mymax + pad); vector > vlimits(1, limits); uInt64 mynpts = knownNpts.null() ? getNPts() : *knownNpts; vector vmynpts(1, mynpts); return _dataFromSingleBins( vmynpts, maxArraySize, vlimits, vindices, nBins )[0]; } CASA_STATD void ClassicalStatistics::_initIterators() { ThrowIf(! _hasData, "No data sets have been added"); if (this->_getDataProvider()) { this->_getDataProvider()->reset(); } else { _dataCount = 0; const vector& data = this->_getData(); _diter = data.begin(); _dend = data.end(); const vector& dataStrides = this->_getDataStrides(); _dsiter = dataStrides.begin(); const vector& counts = this->_getCounts(); _citer = counts.begin(); _masks = this->_getMasks(); _weights = this->_getWeights(); _ranges = this->_getRanges(); _isIncludeRanges = this->_getIsIncludeRanges(); } _hasRanges = False; _myRanges.clear(); _myIsInclude = False; _hasMask = False; _hasWeights = False; } CASA_STATD void ClassicalStatistics::_initLoopVars() { StatsDataProvider *dataProvider = this->_getDataProvider(); if (dataProvider) { _myData = dataProvider->getData(); _myCount = dataProvider->getCount(); _myStride = dataProvider->getStride(); _hasRanges = dataProvider->hasRanges(); if (_hasRanges) { _myRanges = dataProvider->getRanges(); _myIsInclude = dataProvider->isInclude(); } _hasMask = dataProvider->hasMask(); if (_hasMask) { _myMask = dataProvider->getMask(); _maskStride = dataProvider->getMaskStride(); } _hasWeights = dataProvider->hasWeights(); if (_hasWeights) { _myWeights = dataProvider->getWeights(); } } else { _myData = *_diter; _myCount = *_citer; _myStride = *_dsiter; typename std::map::const_iterator rangeI = _ranges.find(_dataCount); _hasRanges = rangeI != _ranges.end(); if (_hasRanges) { _myRanges = rangeI->second; _myIsInclude = _isIncludeRanges.find(_dataCount)->second; } typename std::map::const_iterator maskI = _masks.find(_dataCount); _hasMask = maskI != _masks.end(); if (_hasMask) { _myMask = maskI->second; _maskStride = this->_getMaskStrides().find(_dataCount)->second; } _hasWeights = _weights.find(_dataCount) != _weights.end(); if (_hasWeights) { _myWeights = _weights.find(_dataCount)->second; } } } CASA_STATD void ClassicalStatistics::_initThreadVars( uInt& nBlocks, uInt64& extra, uInt& nthreads, PtrHolder& dataIter, PtrHolder& maskIter, PtrHolder& weightsIter, PtrHolder& offset, uInt nThreadsMax ) const { uInt n = ClassicalStatisticsData::CACHE_PADDING*nThreadsMax; dataIter.set(new DataIterator[n], True); maskIter.set(new MaskIterator[n], True); weightsIter.set(new WeightsIterator[n], True); offset.set(new uInt64[n], True); nBlocks = _myCount/ClassicalStatisticsData::BLOCK_SIZE; extra = _myCount % ClassicalStatisticsData::BLOCK_SIZE; if (extra > 0) { ++nBlocks; } nthreads = min(nThreadsMax, nBlocks); for (uInt tid=0; tid::_isNptsSmallerThan( vector& unsortedAry, uInt maxArraySize ) { _initIterators(); Bool limitReached = False; while (True) { _initLoopVars(); if (_hasWeights) { if (_hasMask) { if (_hasRanges) { limitReached = _populateTestArray( unsortedAry, _myData, _myWeights, _myCount, _myStride, _myMask, _maskStride, _myRanges, _myIsInclude, maxArraySize ); } else { limitReached = _populateTestArray( unsortedAry, _myData, _myWeights, _myCount, _myStride, _myMask, _maskStride, maxArraySize ); } } else if (_hasRanges) { limitReached = _populateTestArray( unsortedAry, _myData, _myWeights, _myCount, _myStride, _myRanges, _myIsInclude, maxArraySize ); } else { // has weights, but no mask nor ranges limitReached = _populateTestArray( unsortedAry, _myData, _myWeights, _myCount, _myStride, maxArraySize ); } } else if (_hasMask) { // this data set has no weights, but does have a mask if (_hasRanges) { limitReached = _populateTestArray( unsortedAry, _myData, _myCount, _myStride, _myMask, _maskStride, _myRanges, _myIsInclude, maxArraySize ); } else { limitReached = _populateTestArray( unsortedAry, _myData, _myCount, _myStride, _myMask, _maskStride, maxArraySize ); } } else if (_hasRanges) { // this data set has no weights no mask, but does have a set of ranges // associated with it limitReached = _populateTestArray( unsortedAry, _myData, _myCount, _myStride, _myRanges, _myIsInclude, maxArraySize ); } 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. limitReached = _populateTestArray( unsortedAry, _myData, _myCount, _myStride, maxArraySize ); } if (limitReached) { unsortedAry.clear(); return False; } if (_increment(False)) { break; } } _getStatsData().npts = unsortedAry.size(); return True; } CASA_STATD void ClassicalStatistics::_makeBins( typename StatisticsUtilities::BinDesc& bins, AccumType minData, AccumType maxData, uInt maxBins, Bool allowPad ) { bins.nBins = maxBins; bins.minLimit = minData; AccumType maxLimit = maxData; if (allowPad) { AccumType pad = (maxData - minData)/1e3; if (pad == (AccumType)0) { // try to handle Int like AccumTypes pad = AccumType(1); } bins.minLimit -= pad; maxLimit += pad; } bins.binWidth = (maxLimit - bins.minLimit)/(AccumType)bins.nBins; } #define _minMaxCode \ if (! mymin.null()) { \ if (*datum < *mymin) { \ *mymin = *datum; \ } \ else if (*datum > *mymax) { \ *mymax = *datum; \ } \ } \ else { \ mymin = new AccumType(*datum); \ mymax = new AccumType(*datum); \ } CASA_STATD void ClassicalStatistics::_minMax( CountedPtr& mymin, CountedPtr& mymax, const DataIterator& dataBegin, Int64 nr, uInt dataStride ) const { DataIterator datum = dataBegin; Int64 count = 0; while (count < nr) { _minMaxCode StatisticsIncrementer::increment( datum, count, dataStride ); } } CASA_STATD void ClassicalStatistics::_minMax( CountedPtr& mymin, CountedPtr& mymax, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) const { DataIterator datum = dataBegin; Int64 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 ) ) { _minMaxCode } StatisticsIncrementer::increment( datum, count, dataStride ); } } CASA_STATD void ClassicalStatistics::_minMax( CountedPtr& mymin, CountedPtr& mymax, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) const { DataIterator datum = dataBegin; MaskIterator mask = maskBegin; Int64 count = 0; while (count < nr) { if (*mask) { _minMaxCode } StatisticsIncrementer::increment( datum, count, mask, dataStride, maskStride ); } } CASA_STATD void ClassicalStatistics::_minMax( CountedPtr& mymin, CountedPtr& mymax, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) const { DataIterator datum = dataBegin; MaskIterator mask = maskBegin; Int64 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 ) ) { _minMaxCode } StatisticsIncrementer::increment( datum, count, mask, dataStride, maskStride ); } } CASA_STATD void ClassicalStatistics::_minMax( CountedPtr& mymin, CountedPtr& mymax, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride ) const { DataIterator datum = dataBegin; WeightsIterator weight = weightsBegin; Int64 count = 0; while (count < nr) { if (*weight > 0) { _minMaxCode } StatisticsIncrementer::increment( datum, count, weight, dataStride ); } } CASA_STATD void ClassicalStatistics::_minMax( CountedPtr& mymin, CountedPtr& mymax, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) const { DataIterator datum = dataBegin; WeightsIterator weight = weightsBegin; Int64 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 ) ) { _minMaxCode } StatisticsIncrementer::increment( datum, count, weight, dataStride ); } } CASA_STATD void ClassicalStatistics::_minMax( CountedPtr& mymin, CountedPtr& mymax, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) const { DataIterator datum = dataBegin; WeightsIterator weight = weightsBegin; MaskIterator mask = maskBegin; Int64 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 ) ) { _minMaxCode } StatisticsIncrementer::increment( datum, count, weight, mask, dataStride, maskStride ); } } CASA_STATD void ClassicalStatistics::_minMax( CountedPtr& mymin, CountedPtr& mymax, const DataIterator& dataBegin, const WeightsIterator& weightBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) const { DataIterator datum = dataBegin; WeightsIterator weight = weightBegin; MaskIterator mask = maskBegin; Int64 count = 0; while (count < nr) { if (*mask && *weight > 0) { _minMaxCode } 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 - *_statsData.median) : *datum; \ ary.push_back(myDatum); CASA_STATD void ClassicalStatistics::_populateArray( vector& ary, const DataIterator& dataBegin, Int64 nr, uInt dataStride ) const { Int64 count = 0; DataIterator datum = dataBegin; while (count < nr) { _populateArrayCode1 StatisticsIncrementer::increment( datum, count, dataStride ); } } CASA_STATD void ClassicalStatistics::_populateArray( vector& ary, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) const { Int64 count = 0; DataIterator datum = dataBegin; typename DataRanges::const_iterator beginRange = ranges.begin(); typename DataRanges::const_iterator endRange = ranges.end(); while (count < nr) { if ( StatisticsUtilities::includeDatum( *datum, beginRange, endRange, isInclude ) ) { _populateArrayCode1 } StatisticsIncrementer::increment( datum, count, dataStride ); } } CASA_STATD void ClassicalStatistics::_populateArray( vector& ary, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) const { Int64 count = 0; DataIterator datum = dataBegin; MaskIterator mask = maskBegin; while (count < nr) { if (*mask) { _populateArrayCode1 } StatisticsIncrementer::increment( datum, count, mask, dataStride, maskStride ); } } CASA_STATD void ClassicalStatistics::_populateArray( vector& ary, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) const { Int64 count = 0; DataIterator datum = dataBegin; MaskIterator mask = maskBegin; 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 ) ) { _populateArrayCode1 } StatisticsIncrementer::increment( datum, count, mask, dataStride, maskStride ); } } CASA_STATD void ClassicalStatistics::_populateArray( vector& ary, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride ) const { DataIterator datum = dataBegin; WeightsIterator weight = weightsBegin; Int64 count = 0; while (count < nr) { if (*weight > 0) { _populateArrayCode1 } StatisticsIncrementer::increment( datum, count, weight, dataStride ); } } CASA_STATD void ClassicalStatistics::_populateArray( vector& ary, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) const { DataIterator datum = dataBegin; WeightsIterator weight = weightsBegin; Int64 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 ) ) { _populateArrayCode1 } StatisticsIncrementer::increment( datum, count, weight, dataStride ); } } CASA_STATD void ClassicalStatistics::_populateArray( vector& ary, const DataIterator& dataBegin, const WeightsIterator& weightBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) const { DataIterator datum = dataBegin; WeightsIterator weight = weightBegin; MaskIterator mask = maskBegin; Int64 count = 0; while (count < nr) { if (*mask && *weight > 0) { _populateArrayCode1 } StatisticsIncrementer::increment( datum, count, weight, mask, dataStride, maskStride ); } } CASA_STATD void ClassicalStatistics::_populateArray( vector& ary, const DataIterator& dataBegin, const WeightsIterator& weightBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) const { DataIterator datum = dataBegin; WeightsIterator weight = weightBegin; MaskIterator mask = maskBegin; Int64 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 ) ) { _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 - *_statsData.median) : *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 ClassicalStatistics::_populateArrays( vector >& arys, uInt64& currentCount, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const vector > &includeLimits, uInt64 maxCount ) const { typename vector >::iterator bArys = arys.begin(); typename vector >::iterator iArys = bArys; typename vector >::const_iterator bIncludeLimits = includeLimits.begin(); typename vector >::const_iterator iIncludeLimits = bIncludeLimits; typename vector >::const_iterator eIncludeLimits = includeLimits.end(); Int64 count = 0; DataIterator datum = dataBegin; while (count < nr) { _populateArraysCode StatisticsIncrementer::increment( datum, count, dataStride ); } } CASA_STATD void ClassicalStatistics::_populateArrays( vector >& arys, uInt64& currentCount, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude, const vector > &includeLimits, uInt64 maxCount ) const { typename vector >::iterator bArys = arys.begin(); typename vector >::iterator iArys = bArys; typename vector >::const_iterator bIncludeLimits = includeLimits.begin(); typename vector >::const_iterator iIncludeLimits = bIncludeLimits; typename vector >::const_iterator eIncludeLimits = includeLimits.end(); Int64 count = 0; DataIterator datum = dataBegin; typename DataRanges::const_iterator beginRange = ranges.begin(); typename DataRanges::const_iterator endRange = ranges.end(); while (count < nr) { if ( StatisticsUtilities::includeDatum( *datum, beginRange, endRange, isInclude ) ) { _populateArraysCode } StatisticsIncrementer::increment( datum, count, dataStride ); } } CASA_STATD void ClassicalStatistics::_populateArrays( vector >& arys, uInt64& currentCount, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const vector > &includeLimits, uInt64 maxCount ) const { typename vector >::iterator bArys = arys.begin(); typename vector >::iterator iArys = bArys; typename vector >::const_iterator bIncludeLimits = includeLimits.begin(); typename vector >::const_iterator iIncludeLimits = bIncludeLimits; typename vector >::const_iterator eIncludeLimits = includeLimits.end(); Int64 count = 0; DataIterator datum = dataBegin; MaskIterator mask = maskBegin; while (count < nr) { if (*mask) { _populateArraysCode } StatisticsIncrementer::increment( datum, count, mask, dataStride, maskStride ); } } CASA_STATD void ClassicalStatistics::_populateArrays( vector >& arys, uInt64& currentCount, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude, const vector > &includeLimits, uInt64 maxCount ) const { typename vector >::iterator bArys = arys.begin(); typename vector >::iterator iArys = bArys; typename vector >::const_iterator bIncludeLimits = includeLimits.begin(); typename vector >::const_iterator iIncludeLimits = bIncludeLimits; typename vector >::const_iterator eIncludeLimits = includeLimits.end(); Int64 count = 0; DataIterator datum = dataBegin; MaskIterator mask = maskBegin; 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 ) ) { _populateArraysCode } StatisticsIncrementer::increment( datum, count, mask, dataStride, maskStride ); } } CASA_STATD void ClassicalStatistics::_populateArrays( vector >& arys, uInt64& currentCount, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride, const vector > &includeLimits, uInt64 maxCount ) const { typename vector >::iterator bArys = arys.begin(); typename vector >::iterator iArys = bArys; typename vector >::const_iterator bIncludeLimits = includeLimits.begin(); typename vector >::const_iterator iIncludeLimits = bIncludeLimits; typename vector >::const_iterator eIncludeLimits = includeLimits.end(); DataIterator datum = dataBegin; WeightsIterator weight = weightsBegin; Int64 count = 0; while (count < nr) { if (*weight > 0) { _populateArraysCode } StatisticsIncrementer::increment( datum, count, weight, dataStride ); } } CASA_STATD void ClassicalStatistics::_populateArrays( vector >& arys, uInt64& currentCount, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude, const vector > &includeLimits, uInt64 maxCount ) const { typename vector >::iterator bArys = arys.begin(); typename vector >::iterator iArys = bArys; typename vector >::const_iterator bIncludeLimits = includeLimits.begin(); typename vector >::const_iterator iIncludeLimits = bIncludeLimits; typename vector >::const_iterator eIncludeLimits = includeLimits.end(); DataIterator datum = dataBegin; WeightsIterator weight = weightsBegin; Int64 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 ) ) { _populateArraysCode } StatisticsIncrementer::increment( datum, count, weight, dataStride ); } } CASA_STATD void ClassicalStatistics::_populateArrays( vector >& arys, uInt64& currentCount, const DataIterator& dataBegin, const WeightsIterator& weightBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const vector > &includeLimits, uInt64 maxCount ) const { typename vector >::iterator bArys = arys.begin(); typename vector >::iterator iArys = bArys; typename vector >::const_iterator bIncludeLimits = includeLimits.begin(); typename vector >::const_iterator iIncludeLimits = bIncludeLimits; typename vector >::const_iterator eIncludeLimits = includeLimits.end(); DataIterator datum = dataBegin; WeightsIterator weight = weightBegin; MaskIterator mask = maskBegin; Int64 count = 0; while (count < nr) { if (*mask && *weight > 0) { _populateArraysCode } StatisticsIncrementer::increment( datum, count, weight, mask, dataStride, maskStride ); } } CASA_STATD void ClassicalStatistics::_populateArrays( vector >& arys, uInt64& currentCount, const DataIterator& dataBegin, const WeightsIterator& weightBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude, const vector > &includeLimits, uInt64 maxCount ) const { typename vector >::iterator bArys = arys.begin(); typename vector >::iterator iArys = bArys; typename vector >::const_iterator bIncludeLimits = includeLimits.begin(); typename vector >::const_iterator iIncludeLimits = bIncludeLimits; typename vector >::const_iterator eIncludeLimits = includeLimits.end(); DataIterator datum = dataBegin; WeightsIterator weight = weightBegin; MaskIterator mask = maskBegin; Int64 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 ) ) { _populateArraysCode } StatisticsIncrementer::increment( datum, count, weight, mask, dataStride, maskStride ); } } CASA_STATD Bool ClassicalStatistics::_populateTestArray( vector& ary, const DataIterator& dataBegin, Int64 nr, uInt dataStride, uInt maxElements ) const { if (ary.size() + nr > maxElements) { return True; } Int64 count = 0; DataIterator datum = dataBegin; while (count < nr) { ary.push_back(_doMedAbsDevMed ? abs((AccumType)*datum - *_statsData.median) : *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 - *_statsData.median) : *datum); \ ++npts; \ if (npts > maxElements) { \ return True; \ } CASA_STATD Bool ClassicalStatistics::_populateTestArray( vector& ary, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude, uInt maxElements ) const { Int64 count = 0; uInt npts = ary.size(); DataIterator datum = dataBegin; typename DataRanges::const_iterator beginRange = ranges.begin(); typename DataRanges::const_iterator endRange = ranges.end(); while (count < nr) { if ( StatisticsUtilities::includeDatum( *datum, beginRange, endRange, isInclude ) ) { _PopulateTestArrayCode } StatisticsIncrementer::increment( datum, count, dataStride ); } return False; } CASA_STATD Bool ClassicalStatistics::_populateTestArray( vector& ary, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, uInt maxElements ) const { Int64 count = 0; DataIterator datum = dataBegin; MaskIterator mask = maskBegin; uInt npts = ary.size(); while (count < nr) { if (*mask) { _PopulateTestArrayCode } StatisticsIncrementer::increment( datum, count, mask, dataStride, maskStride ); } return False; } CASA_STATD Bool ClassicalStatistics::_populateTestArray( vector& ary, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude, uInt maxElements ) const { Int64 count = 0; DataIterator datum = dataBegin; MaskIterator mask = maskBegin; uInt npts = ary.size(); 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 ) ) { _PopulateTestArrayCode } StatisticsIncrementer::increment( datum, count, mask, dataStride, maskStride ); } return False; } CASA_STATD Bool ClassicalStatistics::_populateTestArray( vector& ary, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride, uInt maxElements ) const { DataIterator datum = dataBegin; WeightsIterator weight = weightsBegin; Int64 count = 0; uInt npts = ary.size(); while (count < nr) { if (*weight > 0) { _PopulateTestArrayCode } StatisticsIncrementer::increment( datum, count, weight, dataStride ); } return False; } CASA_STATD Bool ClassicalStatistics::_populateTestArray( vector& ary, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude, uInt maxElements ) const { DataIterator datum = dataBegin; WeightsIterator weight = weightsBegin; Int64 count = 0; typename DataRanges::const_iterator beginRange = ranges.begin(); typename DataRanges::const_iterator endRange = ranges.end(); 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 ClassicalStatistics::_populateTestArray( vector& ary, const DataIterator& dataBegin, const WeightsIterator& weightBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, uInt maxElements ) const { DataIterator datum = dataBegin; WeightsIterator weight = weightBegin; MaskIterator mask = maskBegin; Int64 count = 0; uInt npts = ary.size(); while (count < nr) { if (*mask && *weight > 0) { _PopulateTestArrayCode } StatisticsIncrementer::increment( datum, count, weight, mask, dataStride, maskStride ); } return False; } CASA_STATD Bool ClassicalStatistics::_populateTestArray( vector& ary, const DataIterator& dataBegin, const WeightsIterator& weightBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude, uInt maxElements ) const { DataIterator datum = dataBegin; WeightsIterator weight = weightBegin; MaskIterator mask = maskBegin; Int64 count = 0; typename DataRanges::const_iterator beginRange = ranges.begin(); typename DataRanges::const_iterator endRange = ranges.end(); 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 void ClassicalStatistics::_updateDataProviderMaxMin( const StatsData& threadStats ) { StatsDataProvider *dataProvider = this->_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. StatsData& stats = _getStatsData(); Bool same = &threadStats == &stats; if ( _idataset == threadStats.maxpos.first && (stats.max.null() || *threadStats.max > *stats.max) ) { if (! same) { // make certain to make a copy, do not assign // one pointer to another stats.maxpos = threadStats.maxpos; stats.max = new AccumType(*threadStats.max); } dataProvider->updateMaxPos(stats.maxpos); } if ( _idataset == threadStats.minpos.first && (stats.min.null() || (*threadStats.min) < (*stats.min)) ) { if (! same) { // make certain to make a copy of the value, do not assign // one pointer to another stats.minpos = threadStats.minpos; stats.min = new AccumType(*threadStats.min); } dataProvider->updateMinPos(stats.minpos); } } CASA_STATD void ClassicalStatistics::_unweightedStats( StatsData& stats, uInt64& ngood, LocationType& location, const DataIterator& dataBegin, Int64 nr, uInt dataStride ) { DataIterator datum = dataBegin; Int64 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, Int64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) { DataIterator datum = dataBegin; Int64 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 ) ) { _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, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) { DataIterator datum = dataBegin; MaskIterator mask = maskBegin; Int64 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, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) { DataIterator datum = dataBegin; MaskIterator mask = maskBegin; Int64 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 ) ) { _accumulate( stats, *datum, location ); ++ngood; } StatisticsIncrementer::increment( datum, count, mask, dataStride, maskStride ); location.second += dataStride; } } CASA_STATD Bool ClassicalStatistics::_valuesFromSortedArray( std::map& values, CountedPtr knownNpts, 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); vector myArray; if (_doMedAbsDevMed && ! this->_getSortedArray().empty()) { // make a copy vector pSorted = this->_getSortedArray(); myArray = pSorted; _convertToAbsDevMedArray(myArray, *_getStatsData().median); } if (! _doMedAbsDevMed) { myArray = this->_getSortedArray(); } uInt64 myNpts = _getStatsData().npts > 0 ? (uInt64)_getStatsData().npts : knownNpts.null() ? 0 : *knownNpts; ThrowIf(myNpts == 0, "No valid data found"); if (myArray.empty()) { if (myNpts > 0) { // we have already computed npts 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; } } else { // we have to calculate the number of good points if (! this->_getDataProvider()) { // we first get an upper limit by adding up the counts const vector& counts = this->_getCounts(); uInt64 nr = accumulate(counts.begin(), counts.end(), 0); if (nr <= maxArraySize) { // data can be sorted in memory _createDataArray(myArray); } else { return False; } } // last resort. scan through the dataset to determine if npts is small enough // if it is, myArray will be populated with unsorted data if (myArray.empty() && ! _isNptsSmallerThan(myArray, maxArraySize)) { return False; } } } values = StatisticsAlgorithm::_valuesFromArray( myArray, indices ); if (! _doMedAbsDevMed) { if (persistSortedArray) { this->_setSortedArray(myArray); } else { this->_setSortedArray(vector()); } } return True; } CASA_STATD void ClassicalStatistics::_weightedStats( StatsData& stats, LocationType& location, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride ) { DataIterator datum = dataBegin; WeightsIterator weight = weightsBegin; Int64 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, Int64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) { DataIterator datum = dataBegin; WeightsIterator weight = weightsBegin; Int64 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 ) ) { _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, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) { DataIterator datum = dataBegin; WeightsIterator weight = weightsBegin; MaskIterator mask = maskBegin; Int64 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 ) ) { _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, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) { DataIterator datum = dataBegin; WeightsIterator weight = weightBegin; MaskIterator mask = maskBegin; Int64 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-2.4.1/scimath/Mathematics/ClassicalStatisticsData.cc000066400000000000000000000025471321422335000242420ustar00rootroot00000000000000//# 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: aips2-request@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-2.4.1/scimath/Mathematics/ClassicalStatisticsData.h000066400000000000000000000032201321422335000240710ustar00rootroot00000000000000//# 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: aips2-request@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: static const uInt CACHE_PADDING; static const uInt BLOCK_SIZE; ~ClassicalStatisticsData() {} private: // disable default constructors ClassicalStatisticsData() {} ClassicalStatisticsData(const ClassicalStatisticsData& ) {} }; } #endif casacore-2.4.1/scimath/Mathematics/Combinatorics.cc000066400000000000000000000053031321422335000222640ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ // #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; 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. ScopedMutexLock 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-2.4.1/scimath/Mathematics/Combinatorics.h000066400000000000000000000052221321422335000221260ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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) { //# This test is thread-safe. if (n >= _factorialCacheSize) 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 Mutex theirMutex; }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/scimath/Mathematics/ConstrainedRangeStatistics.h000066400000000000000000000664611321422335000246470ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: Array.h 21545 2015-01-22 19:36:35Z gervandiepen $ #ifndef SCIMATH_CONSTRAINEDRANGESTATISTICS_H #define SCIMATH_CONSTRAINEDRANGESTATISTICS_H #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. template class ConstrainedRangeStatistics : public ClassicalStatistics { public: virtual ~ConstrainedRangeStatistics(); // copy semantics ConstrainedRangeStatistics& operator=( const ConstrainedRangeStatistics& other ); // // 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. AccumType getMedian( CountedPtr knownNpts=NULL, CountedPtr knownMin=NULL, CountedPtr knownMax=NULL, uInt binningThreshholdSizeBytes=4096*4096, Bool persistSortedArray=False, uInt64 nBins=10000 ); // get the median of the absolute deviation about the median of the data. AccumType getMedianAbsDevMed( CountedPtr knownNpts=NULL, CountedPtr knownMin=NULL, CountedPtr knownMax=NULL, uInt binningThreshholdSizeBytes=4096*4096, Bool persistSortedArray=False, uInt64 nBins=10000 ); // If one needs to compute both the median and quantile values, it is better to call // getMedianAndQuantiles() rather than getMedian() and getQuantiles() seperately, 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. AccumType getMedianAndQuantiles( std::map& quantileToValue, const std::set& quantiles, CountedPtr knownNpts=NULL, CountedPtr knownMin=NULL, CountedPtr knownMax=NULL, uInt binningThreshholdSizeBytes=4096*4096, Bool persistSortedArray=False, uInt64 nBins=10000 ); // Get the specified quantiles. quantiles must be between 0 and 1, // noninclusive. std::map getQuantiles( const std::set& quantiles, CountedPtr knownNpts=NULL, CountedPtr knownMin=NULL, CountedPtr knownMax=NULL, uInt binningThreshholdSizeBytes=4096*4096, Bool persistSortedArray=False, uInt64 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 std::pair getStatisticIndex(StatisticsData::STATS stat); // 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(); protected: ConstrainedRangeStatistics(); // // 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. inline void _accumNpts( uInt64& npts, const DataIterator& dataStart, Int64 nr, uInt dataStride ) const; void _accumNpts( uInt64& npts, const DataIterator& dataStart, Int64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) const; void _accumNpts( uInt64& npts, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) const; void _accumNpts( uInt64& npts, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) const; void _accumNpts( uInt64& npts, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride ) const; void _accumNpts( uInt64& npts, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) const; void _accumNpts( uInt64& npts, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) const; void _accumNpts( uInt64& npts, const DataIterator& dataBegin, const WeightsIterator& weightBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) const; // virtual void _findBins( vector >& binCounts, vector >& sameVal, vector& allSame, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const vector::BinDesc>& binDesc, const vector& maxLimit ) const; virtual void _findBins( vector >& binCounts, vector >& sameVal, vector& allSame, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude, const vector::BinDesc>& binDesc, const vector& maxLimit ) const; virtual void _findBins( vector >& binCounts, vector >& sameVal, vector& allSame, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const vector::BinDesc>& binDesc, const vector& maxLimit ) const; virtual void _findBins( vector >& binCounts, vector >& sameVal, vector& allSame, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude, const vector::BinDesc>& binDesc, const vector& maxLimit ) const; virtual void _findBins( vector >& binCounts, vector >& sameVal, vector& allSame, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride, const vector::BinDesc>& binDesc, const vector& maxLimit ) const ; virtual void _findBins( vector >& binCounts, vector >& sameVal, vector& allSame, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude, const vector::BinDesc>& binDesc, const vector& maxLimit ) const; virtual void _findBins( vector >& binCounts, vector >& sameVal, vector& allSame, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude, const vector::BinDesc>& binDesc, const vector& maxLimit ) const; virtual void _findBins( vector >& binCounts, vector >& sameVal, vector& allSame, const DataIterator& dataBegin, const WeightsIterator& weightBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const vector::BinDesc>& binDesc, const vector& maxLimit ) const; // AccumType _getStatistic(StatisticsData::STATS stat); StatsData _getStatistics(); inline Bool _isInRange(const AccumType& datum) const; // virtual void _minMax( CountedPtr& mymin, CountedPtr& mymax, const DataIterator& dataBegin, Int64 nr, uInt dataStride ) const; virtual void _minMax( CountedPtr& mymin, CountedPtr& mymax, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) const; virtual void _minMax( CountedPtr& mymin, CountedPtr& mymax, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) const; virtual void _minMax( CountedPtr& mymin, CountedPtr& mymax, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) const; virtual void _minMax( CountedPtr& mymin, CountedPtr& mymax, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride ) const; virtual void _minMax( CountedPtr& mymin, CountedPtr& mymax, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) const; virtual void _minMax( CountedPtr& mymin, CountedPtr& mymax, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) const; virtual void _minMax( CountedPtr& mymin, CountedPtr& mymax, const DataIterator& dataBegin, const WeightsIterator& weightBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) 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 void _populateArray( vector& ary, const DataIterator& dataBegin, Int64 nr, uInt dataStride ) const; // ranges void _populateArray( vector& ary, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) const; void _populateArray( vector& ary, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) const; // mask and ranges void _populateArray( vector& ary, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) const; // weights void _populateArray( vector& ary, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride ) const; // weights and ranges void _populateArray( vector& ary, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) const; // weights and mask void _populateArray( vector& ary, const DataIterator& dataBegin, const WeightsIterator& weightBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) const; // weights, mask, ranges void _populateArray( vector& ary, const DataIterator& dataBegin, const WeightsIterator& weightBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) const; // no weights, no mask, no ranges virtual void _populateArrays( vector >& arys, uInt64& currentCount, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const vector > &includeLimits, uInt64 maxCount ) const; // ranges virtual void _populateArrays( vector >& arys, uInt64& currentCount, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude, const vector > &includeLimits, uInt64 maxCount ) const; virtual void _populateArrays( vector >& arys, uInt64& currentCount, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const vector > &includeLimits, uInt64 maxCount ) const; // mask and ranges virtual void _populateArrays( vector >& arys, uInt64& currentCount, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude, const vector > &includeLimits, uInt64 maxCount ) const; // weights virtual void _populateArrays( vector >& arys, uInt64& currentCount, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride, const vector > &includeLimits, uInt64 maxCount ) const; // weights and ranges virtual void _populateArrays( vector >& arys, uInt64& currentCount, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude, const vector > &includeLimits, uInt64 maxCount ) const; // weights and mask virtual void _populateArrays( vector >& arys, uInt64& currentCount, const DataIterator& dataBegin, const WeightsIterator& weightBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const vector > &includeLimits, uInt64 maxCount ) const; // weights, mask, ranges virtual void _populateArrays( vector >& arys, uInt64& currentCount, const DataIterator& dataBegin, const WeightsIterator& weightBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude, const vector > &includeLimits, uInt64 maxCount ) const; // // // no weights, no mask, no ranges Bool _populateTestArray( vector& ary, const DataIterator& dataBegin, Int64 nr, uInt dataStride, uInt maxElements ) const; // ranges Bool _populateTestArray( vector& ary, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude, uInt maxElements ) const; // mask Bool _populateTestArray( vector& ary, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, uInt maxElements ) const; // mask and ranges Bool _populateTestArray( vector& ary, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude, uInt maxElements ) const; // weights Bool _populateTestArray( vector& ary, const DataIterator& dataBegin, const WeightsIterator& weightBegin, Int64 nr, uInt dataStride, uInt maxElements ) const; // weights and ranges Bool _populateTestArray( vector& ary, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude, uInt maxElements ) const; // weights and mask Bool _populateTestArray( vector& ary, const DataIterator& dataBegin, const WeightsIterator& weightBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, uInt maxElements ) const; // weights, mask, ranges Bool _populateTestArray( vector& ary, const DataIterator& dataBegin, const WeightsIterator& weightBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude, uInt maxElements ) const; // inline void _setRange(CountedPtr > r) { this->_clearStats(); _range = r; } // derived classes need to implement how to set their respective range virtual void _setRange() = 0; /* // // no weights, no mask, no ranges void _unweightedStats( StatsData& stats, uInt64& ngood, AccumType& mymin, AccumType& mymax, Int64& minpos, Int64& maxpos, const DataIterator& dataBegin, Int64 nr, uInt dataStride ); // no weights, no mask void _unweightedStats( StatsData& stats, uInt64& ngood, AccumType& mymin, AccumType& mymax, Int64& minpos, Int64& maxpos, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ); void _unweightedStats( StatsData& stats, uInt64& ngood, AccumType& mymin, AccumType& mymax, Int64& minpos, Int64& maxpos, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ); void _unweightedStats( StatsData& stats, uInt64& ngood, AccumType& mymin, AccumType& mymax, Int64& minpos, Int64& maxpos, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ); // // // has weights, but no mask, no ranges void _weightedStats( StatsData& stats, AccumType& mymin, AccumType& mymax, Int64& minpos, Int64& maxpos, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride ); void _weightedStats( StatsData& stats, AccumType& mymin, AccumType& mymax, Int64& minpos, Int64& maxpos, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ); void _weightedStats( StatsData& stats, AccumType& mymin, AccumType& mymax, Int64& minpos, Int64& maxpos, const DataIterator& dataBegin, const WeightsIterator& weightBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ); void _weightedStats( StatsData& stats, AccumType& mymin, AccumType& mymax, Int64& minpos, Int64& maxpos, const DataIterator& dataBegin, const WeightsIterator& weightBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ); // */ // // no weights, no mask, no ranges void _unweightedStats( StatsData& stats, uInt64& ngood, /* AccumType& mymin, AccumType& mymax, LocationType& minpos, LocationType& maxpos, */ LocationType& location, const DataIterator& dataBegin, Int64 nr, uInt dataStride ); // no weights, no mask void _unweightedStats( StatsData& stats, uInt64& ngood, /* AccumType& mymin, AccumType& mymax, LocationType& minpos, LocationType& maxpos, */ LocationType& location, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ); void _unweightedStats( StatsData& stats, uInt64& ngood, /* AccumType& mymin, AccumType& mymax, LocationType& minpos, LocationType& maxpos, */ LocationType& location, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ); void _unweightedStats( StatsData& stats, uInt64& ngood, /* AccumType& mymin, AccumType& mymax, LocationType& minpos, LocationType& maxpos, */ LocationType& location, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ); // // // has weights, but no mask, no ranges void _weightedStats( StatsData& stats, /* AccumType& mymin, AccumType& mymax, LocationType& minpos, LocationType& maxpos, */LocationType& location, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride ); void _weightedStats( StatsData& stats, /* AccumType& mymin, AccumType& mymax, LocationType& minpos, LocationType& maxpos, */ LocationType& location, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ); void _weightedStats( StatsData& stats, /* AccumType& mymin, AccumType& mymax, LocationType& minpos, LocationType& maxpos, */ LocationType& location, const DataIterator& dataBegin, const WeightsIterator& weightBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ); void _weightedStats( StatsData& stats, /* AccumType& mymin, AccumType& mymax, LocationType& minpos, LocationType& maxpos, */ LocationType& location, const DataIterator& dataBegin, const WeightsIterator& weightBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ); // private: CountedPtr > _range; Bool _doMedAbsDevMed; }; } #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif #endif casacore-2.4.1/scimath/Mathematics/ConstrainedRangeStatistics.tcc000066400000000000000000002052241321422335000251610ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: Array.h 21545 2015-01-22 19:36:35Z gervandiepen $ #ifndef SCIMATH_CONSTRAINEDRANGESTATISTICS_TCC #define SCIMATH_CONSTRAINEDRANGESTATISTICS_TCC #include #include #include namespace casacore { // min > max indicates that these quantities have not be calculated CASA_STATD ConstrainedRangeStatistics::ConstrainedRangeStatistics() : ClassicalStatistics(), _range(), _doMedAbsDevMed(False) /*, _median()*/ /*, _npts(0), _max(), _min(), _maxpos(-1, -1), _minpos(-1, -1) */ { reset(); } CASA_STATD ConstrainedRangeStatistics::~ConstrainedRangeStatistics() {} CASA_STATD ConstrainedRangeStatistics& ConstrainedRangeStatistics::operator=( const ConstrainedRangeStatistics& other ) { if (this == &other) { return *this; } ClassicalStatistics::operator=(other); _range = other._range; _doMedAbsDevMed = other._doMedAbsDevMed; //_median = other._median.null() ? NULL : new AccumType(*other._median); return *this; } CASA_STATD AccumType ConstrainedRangeStatistics::getMedian( CountedPtr knownNpts, CountedPtr knownMin, CountedPtr knownMax, uInt binningThreshholdSizeBytes, Bool persistSortedArray, uInt64 nBins ) { if (this->_getStatsData().median.null()) { _setRange(); this->_getStatsData().median = new AccumType( ClassicalStatistics::getMedian( knownNpts, knownMin, knownMax, binningThreshholdSizeBytes, persistSortedArray, nBins ) ); } return *this->_getStatsData().median; } CASA_STATD AccumType ConstrainedRangeStatistics::getMedianAbsDevMed( CountedPtr knownNpts, CountedPtr knownMin, CountedPtr knownMax, uInt binningThreshholdSizeBytes, Bool persistSortedArray, uInt64 nBins ) { _setRange(); if (this->_getStatsData().median.null()) { // sets _median, we can discard the return value this->getMedian(); } _doMedAbsDevMed = True; AccumType medabsdevmed = ClassicalStatistics::getMedianAbsDevMed( knownNpts, knownMin, knownMax, binningThreshholdSizeBytes, persistSortedArray, nBins ); _doMedAbsDevMed = False; return medabsdevmed; } CASA_STATD AccumType ConstrainedRangeStatistics::getMedianAndQuantiles( std::map& quantileToValue, const std::set& quantiles, CountedPtr knownNpts, CountedPtr knownMin, CountedPtr knownMax, uInt binningThreshholdSizeBytes, Bool persistSortedArray, uInt64 nBins ) { _setRange(); return ClassicalStatistics::getMedianAndQuantiles( quantileToValue, quantiles, knownNpts, knownMin, knownMax, binningThreshholdSizeBytes, persistSortedArray, nBins ); } CASA_STATD void ConstrainedRangeStatistics::getMinMax( AccumType& mymin, AccumType& mymax ) { _setRange(); return ClassicalStatistics::getMinMax( mymin, mymax ); } CASA_STATD uInt64 ConstrainedRangeStatistics::getNPts() { _setRange(); return ClassicalStatistics::getNPts(); } CASA_STATD std::map ConstrainedRangeStatistics::getQuantiles( const std::set& quantiles, CountedPtr knownNpts, CountedPtr knownMin, CountedPtr knownMax, uInt binningThreshholdSizeBytes, Bool persistSortedArray, uInt64 nBins ) { _setRange(); return ClassicalStatistics::getQuantiles( quantiles, knownNpts, knownMin, knownMax, binningThreshholdSizeBytes, persistSortedArray, nBins ); } CASA_STATD std::pair ConstrainedRangeStatistics::getStatisticIndex( StatisticsData::STATS stat ) { _setRange(); return ClassicalStatistics::getStatisticIndex(stat); } CASA_STATD void ConstrainedRangeStatistics::reset() { _range = NULL; _doMedAbsDevMed = False; //_median = NULL; ClassicalStatistics::reset(); } CASA_STATD void ConstrainedRangeStatistics::_accumNpts( uInt64& npts, const DataIterator& dataBegin, Int64 nr, uInt dataStride ) const { DataIterator datum = dataBegin; Int64 count = 0; while (count < nr) { if (_isInRange(*datum)) { ++npts; } StatisticsIncrementer::increment( datum, count, dataStride ); } } CASA_STATD void ConstrainedRangeStatistics::_accumNpts( uInt64& npts, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) const { DataIterator datum = dataBegin; Int64 count = 0; typename DataRanges::const_iterator beginRange = ranges.begin(); typename DataRanges::const_iterator endRange = ranges.end(); while (count < nr) { if ( _isInRange(*datum) && StatisticsUtilities::includeDatum( *datum, beginRange, endRange, isInclude ) ) { ++npts; } StatisticsIncrementer::increment( datum, count, dataStride ); } } CASA_STATD void ConstrainedRangeStatistics::_accumNpts( uInt64& npts, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) const { DataIterator datum = dataBegin; MaskIterator mask = maskBegin; Int64 count = 0; while (count < nr) { if (*mask && _isInRange(*datum)) { ++npts; } StatisticsIncrementer::increment( datum, count, mask, dataStride, maskStride ); } } CASA_STATD void ConstrainedRangeStatistics::_accumNpts( uInt64& npts, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) const { DataIterator datum = dataBegin; MaskIterator mask = maskBegin; Int64 count = 0; typename DataRanges::const_iterator beginRange = ranges.begin(); typename DataRanges::const_iterator endRange = ranges.end(); while (count < nr) { if ( *mask && _isInRange(*datum) && 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, Int64 nr, uInt dataStride ) const { DataIterator datum = dataBegin; WeightsIterator weight = weightsBegin; Int64 count = 0; while (count < nr) { if (_isInRange(*datum) && *weight > 0) { ++npts; } StatisticsIncrementer::increment( datum, count, weight, dataStride ); } } CASA_STATD void ConstrainedRangeStatistics::_accumNpts( uInt64& npts, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) const { DataIterator datum = dataBegin; WeightsIterator weight = weightsBegin; Int64 count = 0; typename DataRanges::const_iterator beginRange = ranges.begin(); typename DataRanges::const_iterator endRange = ranges.end(); while (count < nr) { if ( _isInRange(*datum) && *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, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) const { DataIterator datum = dataBegin; WeightsIterator weight = weightsBegin; MaskIterator mask = maskBegin; Int64 count = 0; typename DataRanges::const_iterator beginRange = ranges.begin(); typename DataRanges::const_iterator endRange = ranges.end(); while (count < nr) { if ( *mask && _isInRange(*datum) && *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, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) const { DataIterator datum = dataBegin; WeightsIterator weight = weightsBegin; MaskIterator mask = maskBegin; Int64 count = 0; while (count < nr) { if (*mask && _isInRange(*datum) && *weight > 0) { ++npts; } StatisticsIncrementer::increment( datum, count, weight, mask, dataStride, maskStride ); } } CASA_STATD Bool ConstrainedRangeStatistics::_isInRange( const AccumType& datum ) const { return datum >= _range->first && datum <= _range->second; } #define _findBinCodeCR \ if (_isInRange(*datum)) { \ AccumType myDatum = _doMedAbsDevMed ? abs((AccumType)*datum - *this->_getStatsData().median) : *datum; \ if (myDatum >= bBinDesc->minLimit && myDatum < *maxLimit.rbegin()) { \ iCounts = bCounts; \ iSameVal = bSameVal; \ iAllSame = bAllSame; \ iBinDesc = bBinDesc; \ iMaxLimit = bMaxLimit; \ while (iBinDesc != eBinDesc) { \ if (myDatum >= iBinDesc->minLimit && myDatum < *iMaxLimit) { \ AccumType idx = (myDatum - iBinDesc->minLimit)/iBinDesc->binWidth; \ ++(*iCounts)[StatisticsUtilities::getInt(idx)]; \ if (*iAllSame) { \ if (iSameVal->null()) { \ *iSameVal = new AccumType(myDatum); \ } \ else { \ *iAllSame = myDatum == *(*iSameVal); \ if (! *iAllSame) { \ *iSameVal = NULL; \ } \ } \ } \ break; \ } \ ++iCounts; \ ++iSameVal; \ ++iAllSame; \ ++iBinDesc; \ ++iMaxLimit; \ } \ } \ } CASA_STATD void ConstrainedRangeStatistics::_findBins( vector >& binCounts, vector >& sameVal, vector& allSame, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const vector::BinDesc>& binDesc, const vector& maxLimit ) const { vector >::iterator bCounts = binCounts.begin(); vector >::iterator iCounts = bCounts; typename vector >::iterator bSameVal = sameVal.begin(); typename vector >::iterator iSameVal = bSameVal; vector::iterator bAllSame = allSame.begin(); vector::iterator iAllSame = bAllSame; typename vector::BinDesc>::const_iterator bBinDesc = binDesc.begin(); typename vector::BinDesc>::const_iterator iBinDesc = bBinDesc; typename vector::BinDesc>::const_iterator eBinDesc = binDesc.end(); typename vector::const_iterator bMaxLimit = maxLimit.begin(); typename vector::const_iterator iMaxLimit = bMaxLimit; DataIterator datum = dataBegin; Int64 count = 0; while (count < nr) { _findBinCodeCR StatisticsIncrementer::increment( datum, count, dataStride ); } } CASA_STATD void ConstrainedRangeStatistics::_findBins( vector >& binCounts, vector >& sameVal, vector& allSame, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude, const vector::BinDesc>& binDesc, const vector& maxLimit ) const { vector >::iterator bCounts = binCounts.begin(); vector >::iterator iCounts = bCounts; typename vector >::iterator bSameVal = sameVal.begin(); typename vector >::iterator iSameVal = bSameVal; vector::iterator bAllSame = allSame.begin(); vector::iterator iAllSame = bAllSame; typename vector::BinDesc>::const_iterator bBinDesc = binDesc.begin(); typename vector::BinDesc>::const_iterator iBinDesc = bBinDesc; typename vector::BinDesc>::const_iterator eBinDesc = binDesc.end(); typename vector::const_iterator bMaxLimit = maxLimit.begin(); typename vector::const_iterator iMaxLimit = bMaxLimit; DataIterator datum = dataBegin; Int64 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 ) ) { _findBinCodeCR } StatisticsIncrementer::increment( datum, count, dataStride ); } } CASA_STATD void ConstrainedRangeStatistics::_findBins( vector >& binCounts, vector >& sameVal, vector& allSame, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const vector::BinDesc>& binDesc, const vector& maxLimit ) const { vector >::iterator bCounts = binCounts.begin(); vector >::iterator iCounts = bCounts; typename vector >::iterator bSameVal = sameVal.begin(); typename vector >::iterator iSameVal = bSameVal; vector::iterator bAllSame = allSame.begin(); vector::iterator iAllSame = bAllSame; typename vector::BinDesc>::const_iterator bBinDesc = binDesc.begin(); typename vector::BinDesc>::const_iterator iBinDesc = bBinDesc; typename vector::BinDesc>::const_iterator eBinDesc = binDesc.end(); typename vector::const_iterator bMaxLimit = maxLimit.begin(); typename vector::const_iterator iMaxLimit = bMaxLimit; DataIterator datum = dataBegin; MaskIterator mask = maskBegin; Int64 count = 0; while (count < nr) { if (*mask) { _findBinCodeCR } StatisticsIncrementer::increment( datum, count, mask, dataStride, maskStride ); } } CASA_STATD void ConstrainedRangeStatistics::_findBins( vector >& binCounts, vector >& sameVal, vector& allSame, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude, const vector::BinDesc>& binDesc, const vector& maxLimit ) const { vector >::iterator bCounts = binCounts.begin(); vector >::iterator iCounts = bCounts; typename vector >::iterator bSameVal = sameVal.begin(); typename vector >::iterator iSameVal = bSameVal; vector::iterator bAllSame = allSame.begin(); vector::iterator iAllSame = bAllSame; typename vector::BinDesc>::const_iterator bBinDesc = binDesc.begin(); typename vector::BinDesc>::const_iterator iBinDesc = bBinDesc; typename vector::BinDesc>::const_iterator eBinDesc = binDesc.end(); typename vector::const_iterator bMaxLimit = maxLimit.begin(); typename vector::const_iterator iMaxLimit = bMaxLimit; DataIterator datum = dataBegin; MaskIterator mask = maskBegin; Int64 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 ) ) { _findBinCodeCR } StatisticsIncrementer::increment( datum, count, mask, dataStride, maskStride ); } } CASA_STATD void ConstrainedRangeStatistics::_findBins( vector >& binCounts, vector >& sameVal, vector& allSame, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride, const vector::BinDesc>& binDesc, const vector& maxLimit ) const { vector >::iterator bCounts = binCounts.begin(); vector >::iterator iCounts = bCounts; typename vector >::iterator bSameVal = sameVal.begin(); typename vector >::iterator iSameVal = bSameVal; vector::iterator bAllSame = allSame.begin(); vector::iterator iAllSame = bAllSame; typename vector::BinDesc>::const_iterator bBinDesc = binDesc.begin(); typename vector::BinDesc>::const_iterator iBinDesc = bBinDesc; typename vector::BinDesc>::const_iterator eBinDesc = binDesc.end(); typename vector::const_iterator bMaxLimit = maxLimit.begin(); typename vector::const_iterator iMaxLimit = bMaxLimit; DataIterator datum = dataBegin; WeightsIterator weight = weightsBegin; Int64 count = 0; while (count < nr) { if (*weight > 0) { _findBinCodeCR } StatisticsIncrementer::increment( datum, count, weight, dataStride ); } } CASA_STATD void ConstrainedRangeStatistics::_findBins( vector >& binCounts, vector >& sameVal, vector& allSame, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude, const vector::BinDesc>& binDesc, const vector& maxLimit ) const { vector >::iterator bCounts = binCounts.begin(); vector >::iterator iCounts = bCounts; typename vector >::iterator bSameVal = sameVal.begin(); typename vector >::iterator iSameVal = bSameVal; vector::iterator bAllSame = allSame.begin(); vector::iterator iAllSame = bAllSame; typename vector::BinDesc>::const_iterator bBinDesc = binDesc.begin(); typename vector::BinDesc>::const_iterator iBinDesc = bBinDesc; typename vector::BinDesc>::const_iterator eBinDesc = binDesc.end(); typename vector::const_iterator bMaxLimit = maxLimit.begin(); typename vector::const_iterator iMaxLimit = bMaxLimit; DataIterator datum = dataBegin; WeightsIterator weight = weightsBegin; Int64 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 ) ) { _findBinCodeCR } StatisticsIncrementer::increment( datum, count, weight, dataStride ); } } CASA_STATD void ConstrainedRangeStatistics::_findBins( vector >& binCounts, vector >& sameVal, vector& allSame, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude, const vector::BinDesc>& binDesc, const vector& maxLimit ) const { vector >::iterator bCounts = binCounts.begin(); vector >::iterator iCounts = bCounts; typename vector >::iterator bSameVal = sameVal.begin(); typename vector >::iterator iSameVal = bSameVal; vector::iterator bAllSame = allSame.begin(); vector::iterator iAllSame = bAllSame; typename vector::BinDesc>::const_iterator bBinDesc = binDesc.begin(); typename vector::BinDesc>::const_iterator iBinDesc = bBinDesc; typename vector::BinDesc>::const_iterator eBinDesc = binDesc.end(); typename vector::const_iterator bMaxLimit = maxLimit.begin(); typename vector::const_iterator iMaxLimit = bMaxLimit; DataIterator datum = dataBegin; WeightsIterator weight = weightsBegin; MaskIterator mask = maskBegin; Int64 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 ) ) { _findBinCodeCR } StatisticsIncrementer::increment( datum, count, weight, mask, dataStride, maskStride ); } } CASA_STATD void ConstrainedRangeStatistics::_findBins( vector >& binCounts, vector >& sameVal, vector& allSame, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const vector::BinDesc>& binDesc, const vector& maxLimit ) const { vector >::iterator bCounts = binCounts.begin(); vector >::iterator iCounts = bCounts; typename vector >::iterator bSameVal = sameVal.begin(); typename vector >::iterator iSameVal = bSameVal; vector::iterator bAllSame = allSame.begin(); vector::iterator iAllSame = bAllSame; typename vector::BinDesc>::const_iterator bBinDesc = binDesc.begin(); typename vector::BinDesc>::const_iterator iBinDesc = bBinDesc; typename vector::BinDesc>::const_iterator eBinDesc = binDesc.end(); typename vector::const_iterator bMaxLimit = maxLimit.begin(); typename vector::const_iterator iMaxLimit = bMaxLimit; DataIterator datum = dataBegin; WeightsIterator weight = weightsBegin; MaskIterator mask = maskBegin; Int64 count = 0; while (count < nr) { if (*mask && *weight > 0) { _findBinCodeCR } StatisticsIncrementer::increment( datum, count, weight, mask, dataStride, maskStride ); } } 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 (_isInRange(*datum)) { \ _minMaxCode \ } CASA_STATD void ConstrainedRangeStatistics::_minMax( CountedPtr& mymin, CountedPtr& mymax, const DataIterator& dataBegin, Int64 nr, uInt dataStride ) const { DataIterator datum = dataBegin; Int64 count = 0; while (count < nr) { _minMaxCodeCR StatisticsIncrementer::increment( datum, count, dataStride ); } } CASA_STATD void ConstrainedRangeStatistics::_minMax( CountedPtr& mymin, CountedPtr& mymax, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) const { DataIterator datum = dataBegin; Int64 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 ) ) { _minMaxCodeCR } StatisticsIncrementer::increment( datum, count, dataStride ); } } CASA_STATD void ConstrainedRangeStatistics::_minMax( CountedPtr& mymin, CountedPtr& mymax, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) const { DataIterator datum = dataBegin; MaskIterator mask = maskBegin; Int64 count = 0; while (count < nr) { if (*mask) { _minMaxCodeCR } StatisticsIncrementer::increment( datum, count, mask, dataStride, maskStride ); } } CASA_STATD void ConstrainedRangeStatistics::_minMax( CountedPtr& mymin, CountedPtr& mymax, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) const { DataIterator datum = dataBegin; MaskIterator mask = maskBegin; Int64 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 ) ) { _minMaxCodeCR } StatisticsIncrementer::increment( datum, count, mask, dataStride, maskStride ); } } CASA_STATD void ConstrainedRangeStatistics::_minMax( CountedPtr& mymin, CountedPtr& mymax, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride ) const { DataIterator datum = dataBegin; WeightsIterator weight = weightsBegin; Int64 count = 0; while (count < nr) { if (*weight > 0) { _minMaxCodeCR } StatisticsIncrementer::increment( datum, count, weight, dataStride ); } } CASA_STATD void ConstrainedRangeStatistics::_minMax( CountedPtr& mymin, CountedPtr& mymax, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) const { DataIterator datum = dataBegin; WeightsIterator weight = weightsBegin; Int64 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 ) ) { _minMaxCodeCR } StatisticsIncrementer::increment( datum, count, weight, dataStride ); } } CASA_STATD void ConstrainedRangeStatistics::_minMax( CountedPtr& mymin, CountedPtr& mymax, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) const { DataIterator datum = dataBegin; WeightsIterator weight = weightsBegin; MaskIterator mask = maskBegin; Int64 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 ) ) { _minMaxCodeCR } StatisticsIncrementer::increment( datum, count, weight, mask, dataStride, maskStride ); } } CASA_STATD void ConstrainedRangeStatistics::_minMax( CountedPtr& mymin, CountedPtr& mymax, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) const { DataIterator datum = dataBegin; WeightsIterator weight = weightsBegin; MaskIterator mask = maskBegin; Int64 count = 0; while (count < nr) { if (*mask && *weight > 0) { _minMaxCodeCR } 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 (_isInRange(*datum)) { \ AccumType myDatum = _doMedAbsDevMed ? abs((AccumType)*datum - *this->_getStatsData().median) : *datum; \ ary.push_back(myDatum); \ } CASA_STATD void ConstrainedRangeStatistics::_populateArray( vector& ary, const DataIterator& dataBegin, Int64 nr, uInt dataStride ) const { Int64 count = 0; DataIterator datum = dataBegin; while (count < nr) { _populateArrayCodeCR1 StatisticsIncrementer::increment( datum, count, dataStride ); } } CASA_STATD void ConstrainedRangeStatistics::_populateArray( vector& ary, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) const { Int64 count = 0; DataIterator datum = dataBegin; typename DataRanges::const_iterator beginRange = ranges.begin(); typename DataRanges::const_iterator endRange = ranges.end(); while (count < nr) { if ( StatisticsUtilities::includeDatum( *datum, beginRange, endRange, isInclude ) ) { _populateArrayCodeCR1 } StatisticsIncrementer::increment( datum, count, dataStride ); } } CASA_STATD void ConstrainedRangeStatistics::_populateArray( vector& ary, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) const { Int64 count = 0; DataIterator datum = dataBegin; MaskIterator mask = maskBegin; while (count < nr) { if (*mask) { _populateArrayCodeCR1 } StatisticsIncrementer::increment( datum, count, mask, dataStride, maskStride ); } } CASA_STATD void ConstrainedRangeStatistics::_populateArray( vector& ary, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) const { Int64 count = 0; DataIterator datum = dataBegin; MaskIterator mask = maskBegin; 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 ) ) { _populateArrayCodeCR1 } StatisticsIncrementer::increment( datum, count, mask, dataStride, maskStride ); } } CASA_STATD void ConstrainedRangeStatistics::_populateArray( vector& ary, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride ) const { DataIterator datum = dataBegin; WeightsIterator weight = weightsBegin; Int64 count = 0; while (count < nr) { if (*weight > 0) { _populateArrayCodeCR1 } StatisticsIncrementer::increment( datum, count, weight, dataStride ); } } CASA_STATD void ConstrainedRangeStatistics::_populateArray( vector& ary, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) const { DataIterator datum = dataBegin; WeightsIterator weight = weightsBegin; Int64 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 ) ) { _populateArrayCodeCR1 } StatisticsIncrementer::increment( datum, count, weight, dataStride ); } } CASA_STATD void ConstrainedRangeStatistics::_populateArray( vector& ary, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) const { DataIterator datum = dataBegin; WeightsIterator weight = weightsBegin; MaskIterator mask = maskBegin; Int64 count = 0; while (count < nr) { if (*mask && *weight > 0) { _populateArrayCodeCR1 } StatisticsIncrementer::increment( datum, count, weight, mask, dataStride, maskStride ); } } CASA_STATD void ConstrainedRangeStatistics::_populateArray( vector& ary, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) const { DataIterator datum = dataBegin; WeightsIterator weight = weightsBegin; MaskIterator mask = maskBegin; Int64 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 ) ) { _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 (_isInRange(*datum)) { \ AccumType myDatum = _doMedAbsDevMed ? abs((AccumType)*datum - *this->_getStatsData().median) : *datum; \ if (myDatum >= includeLimits.begin()->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 ConstrainedRangeStatistics::_populateArrays( vector >& arys, uInt64& currentCount, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const vector > &includeLimits, uInt64 maxCount ) const { typename vector >::iterator bArys = arys.begin(); typename vector >::iterator iArys = bArys; typename vector >::const_iterator bIncludeLimits = includeLimits.begin(); typename vector >::const_iterator iIncludeLimits = bIncludeLimits; typename vector >::const_iterator eIncludeLimits = includeLimits.end(); Int64 count = 0; DataIterator datum = dataBegin; while (count < nr) { _populateArraysCodeCR StatisticsIncrementer::increment( datum, count, dataStride ); } } CASA_STATD void ConstrainedRangeStatistics::_populateArrays( vector >& arys, uInt64& currentCount, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude, const vector > &includeLimits, uInt64 maxCount ) const { typename vector >::iterator bArys = arys.begin(); typename vector >::iterator iArys = bArys; typename vector >::const_iterator bIncludeLimits = includeLimits.begin(); typename vector >::const_iterator iIncludeLimits = bIncludeLimits; typename vector >::const_iterator eIncludeLimits = includeLimits.end(); Int64 count = 0; DataIterator datum = dataBegin; typename DataRanges::const_iterator beginRange = ranges.begin(); typename DataRanges::const_iterator endRange = ranges.end(); while (count < nr) { if ( StatisticsUtilities::includeDatum( *datum, beginRange, endRange, isInclude ) ) { _populateArraysCodeCR } StatisticsIncrementer::increment( datum, count, dataStride ); } } CASA_STATD void ConstrainedRangeStatistics::_populateArrays( vector >& arys, uInt64& currentCount, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const vector > &includeLimits, uInt64 maxCount ) const { typename vector >::iterator bArys = arys.begin(); typename vector >::iterator iArys = bArys; typename vector >::const_iterator bIncludeLimits = includeLimits.begin(); typename vector >::const_iterator iIncludeLimits = bIncludeLimits; typename vector >::const_iterator eIncludeLimits = includeLimits.end(); Int64 count = 0; DataIterator datum = dataBegin; MaskIterator mask = maskBegin; while (count < nr) { if (*mask) { _populateArraysCodeCR } StatisticsIncrementer::increment( datum, count, mask, dataStride, maskStride ); } } CASA_STATD void ConstrainedRangeStatistics::_populateArrays( vector >& arys, uInt64& currentCount, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude, const vector > &includeLimits, uInt64 maxCount ) const { typename vector >::iterator bArys = arys.begin(); typename vector >::iterator iArys = bArys; typename vector >::const_iterator bIncludeLimits = includeLimits.begin(); typename vector >::const_iterator iIncludeLimits = bIncludeLimits; typename vector >::const_iterator eIncludeLimits = includeLimits.end(); Int64 count = 0; DataIterator datum = dataBegin; MaskIterator mask = maskBegin; 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 ) ) { _populateArraysCodeCR } StatisticsIncrementer::increment( datum, count, mask, dataStride, maskStride ); } } CASA_STATD void ConstrainedRangeStatistics::_populateArrays( vector >& arys, uInt64& currentCount, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride, const vector > &includeLimits, uInt64 maxCount ) const { typename vector >::iterator bArys = arys.begin(); typename vector >::iterator iArys = bArys; typename vector >::const_iterator bIncludeLimits = includeLimits.begin(); typename vector >::const_iterator iIncludeLimits = bIncludeLimits; typename vector >::const_iterator eIncludeLimits = includeLimits.end(); DataIterator datum = dataBegin; WeightsIterator weight = weightsBegin; Int64 count = 0; while (count < nr) { if (*weight > 0) { _populateArraysCodeCR } StatisticsIncrementer::increment( datum, count, weight, dataStride ); } } CASA_STATD void ConstrainedRangeStatistics::_populateArrays( vector >& arys, uInt64& currentCount, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude, const vector > &includeLimits, uInt64 maxCount ) const { typename vector >::iterator bArys = arys.begin(); typename vector >::iterator iArys = bArys; typename vector >::const_iterator bIncludeLimits = includeLimits.begin(); typename vector >::const_iterator iIncludeLimits = bIncludeLimits; typename vector >::const_iterator eIncludeLimits = includeLimits.end(); DataIterator datum = dataBegin; WeightsIterator weight = weightsBegin; Int64 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 ) ) { _populateArraysCodeCR } StatisticsIncrementer::increment( datum, count, weight, dataStride ); } } CASA_STATD void ConstrainedRangeStatistics::_populateArrays( vector >& arys, uInt64& currentCount, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const vector > &includeLimits, uInt64 maxCount ) const { typename vector >::iterator bArys = arys.begin(); typename vector >::iterator iArys = bArys; typename vector >::const_iterator bIncludeLimits = includeLimits.begin(); typename vector >::const_iterator iIncludeLimits = bIncludeLimits; typename vector >::const_iterator eIncludeLimits = includeLimits.end(); DataIterator datum = dataBegin; WeightsIterator weight = weightsBegin; MaskIterator mask = maskBegin; Int64 count = 0; while (count < nr) { if (*mask && *weight > 0) { _populateArraysCodeCR } StatisticsIncrementer::increment( datum, count, weight, mask, dataStride, maskStride ); } } CASA_STATD void ConstrainedRangeStatistics::_populateArrays( vector >& arys, uInt64& currentCount, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude, const vector > &includeLimits, uInt64 maxCount ) const { typename vector >::iterator bArys = arys.begin(); typename vector >::iterator iArys = bArys; typename vector >::const_iterator bIncludeLimits = includeLimits.begin(); typename vector >::const_iterator iIncludeLimits = bIncludeLimits; typename vector >::const_iterator eIncludeLimits = includeLimits.end(); DataIterator datum = dataBegin; WeightsIterator weight = weightsBegin; MaskIterator mask = maskBegin; Int64 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 ) ) { _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 (_isInRange(*datum)) { \ ary.push_back(_doMedAbsDevMed ? abs((AccumType)*datum - *this->_getStatsData().median) : *datum); \ ++npts; \ if (npts > maxElements) { \ return True; \ } \ } CASA_STATD Bool ConstrainedRangeStatistics::_populateTestArray( vector& ary, const DataIterator& dataBegin, Int64 nr, uInt dataStride, uInt maxElements ) const { Int64 count = 0; uInt npts = ary.size(); DataIterator datum = dataBegin; while (count < nr) { _PopulateTestArrayCodeCR StatisticsIncrementer::increment( datum, count, dataStride ); } return False; } CASA_STATD Bool ConstrainedRangeStatistics::_populateTestArray( vector& ary, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude, uInt maxElements ) const { Int64 count = 0; uInt npts = ary.size(); DataIterator datum = dataBegin; typename DataRanges::const_iterator beginRange = ranges.begin(); typename DataRanges::const_iterator endRange = ranges.end(); while (count < nr) { if ( StatisticsUtilities::includeDatum( *datum, beginRange, endRange, isInclude ) ) { _PopulateTestArrayCodeCR } StatisticsIncrementer::increment( datum, count, dataStride ); } return False; } CASA_STATD Bool ConstrainedRangeStatistics::_populateTestArray( vector& ary, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, uInt maxElements ) const { Int64 count = 0; DataIterator datum = dataBegin; MaskIterator mask = maskBegin; uInt npts = ary.size(); while (count < nr) { if (*mask) { _PopulateTestArrayCodeCR } StatisticsIncrementer::increment( datum, count, mask, dataStride, maskStride ); } return False; } CASA_STATD Bool ConstrainedRangeStatistics::_populateTestArray( vector& ary, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude, uInt maxElements ) const { Int64 count = 0; DataIterator datum = dataBegin; MaskIterator mask = maskBegin; uInt npts = ary.size(); 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 ) ) { _PopulateTestArrayCodeCR } StatisticsIncrementer::increment( datum, count, mask, dataStride, maskStride ); } return False; } CASA_STATD Bool ConstrainedRangeStatistics::_populateTestArray( vector& ary, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride, uInt maxElements ) const { DataIterator datum = dataBegin; WeightsIterator weight = weightsBegin; Int64 count = 0; uInt npts = ary.size(); while (count < nr) { if (*weight > 0) { _PopulateTestArrayCodeCR } StatisticsIncrementer::increment( datum, count, weight, dataStride ); } return False; } CASA_STATD Bool ConstrainedRangeStatistics::_populateTestArray( vector& ary, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude, uInt maxElements ) const { DataIterator datum = dataBegin; WeightsIterator weight = weightsBegin; Int64 count = 0; typename DataRanges::const_iterator beginRange = ranges.begin(); typename DataRanges::const_iterator endRange = ranges.end(); uInt 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 ConstrainedRangeStatistics::_populateTestArray( vector& ary, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, uInt maxElements ) const { DataIterator datum = dataBegin; WeightsIterator weight = weightsBegin; MaskIterator mask = maskBegin; Int64 count = 0; uInt npts = ary.size(); while (count < nr) { if (*mask && *weight > 0) { _PopulateTestArrayCodeCR } StatisticsIncrementer::increment( datum, count, weight, mask, dataStride, maskStride ); } return False; } CASA_STATD Bool ConstrainedRangeStatistics::_populateTestArray( vector& ary, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude, uInt maxElements ) const { DataIterator datum = dataBegin; WeightsIterator weight = weightsBegin; MaskIterator mask = maskBegin; Int64 count = 0; typename DataRanges::const_iterator beginRange = ranges.begin(); typename DataRanges::const_iterator endRange = ranges.end(); uInt 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; } #define _unweightedStatsCodeCR \ if (_isInRange(*datum)) { \ this->_accumulate(stats, *datum, location); \ ++ngood; \ } CASA_STATD void ConstrainedRangeStatistics::_unweightedStats( StatsData& stats, uInt64& ngood, /* AccumType& mymin, AccumType& mymax, LocationType& minpos, LocationType& maxpos, */ LocationType& location, const DataIterator& dataBegin, Int64 nr, uInt dataStride ) { DataIterator datum = dataBegin; Int64 count = 0; while (count < nr) { _unweightedStatsCodeCR StatisticsIncrementer::increment( datum, count, dataStride ); location.second += dataStride; } } CASA_STATD void ConstrainedRangeStatistics::_unweightedStats( StatsData& stats, uInt64& ngood, /* AccumType& mymin, AccumType& mymax, LocationType& minpos, LocationType& maxpos, */ LocationType& location, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) { DataIterator datum = dataBegin; Int64 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 ) ) { _unweightedStatsCodeCR } StatisticsIncrementer::increment( datum, count, dataStride ); location.second += dataStride; } } CASA_STATD void ConstrainedRangeStatistics::_unweightedStats( StatsData& stats, uInt64& ngood, /* AccumType& mymin, AccumType& mymax, LocationType& minpos, LocationType& maxpos, */ LocationType& location, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) { DataIterator datum = dataBegin; MaskIterator mask = maskBegin; Int64 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, /* AccumType& mymin, AccumType& mymax, LocationType& minpos, LocationType& maxpos, */ LocationType& location, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) { DataIterator datum = dataBegin; MaskIterator mask = maskBegin; Int64 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 ) ) { _unweightedStatsCodeCR } StatisticsIncrementer::increment( datum, count, mask, dataStride, maskStride ); location.second += dataStride; } } // use #define to ensure code is compiled inline #define _weightedStatsCodeCR \ if (_isInRange(*datum)) { \ this->_accumulate(stats, *datum, *weight, location); \ } CASA_STATD void ConstrainedRangeStatistics::_weightedStats( StatsData& stats, /* AccumType& mymin, AccumType& mymax, LocationType& minpos, LocationType& maxpos, */ LocationType& location, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride ) { DataIterator datum = dataBegin; WeightsIterator weight = weightsBegin; Int64 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, /* AccumType& mymin, AccumType& mymax, LocationType& minpos, LocationType& maxpos, */ LocationType& location, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) { DataIterator datum = dataBegin; WeightsIterator weight = weightsBegin; Int64 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 ) ) { _weightedStatsCodeCR } StatisticsIncrementer::increment( datum, count, weight, dataStride ); location.second += dataStride; } } CASA_STATD void ConstrainedRangeStatistics::_weightedStats( StatsData& stats, /* AccumType& mymin, AccumType& mymax, LocationType& minpos, LocationType& maxpos, */ LocationType& location, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) { DataIterator datum = dataBegin; WeightsIterator weight = weightsBegin; MaskIterator mask = maskBegin; Int64 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 ) ) { _weightedStatsCodeCR } StatisticsIncrementer::increment( datum, count, weight, mask, dataStride, maskStride ); location.second += dataStride; } } CASA_STATD void ConstrainedRangeStatistics::_weightedStats( StatsData& stats, /* AccumType& mymin, AccumType& mymax, LocationType& minpos, LocationType& maxpos, */ LocationType& location, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) { DataIterator datum = dataBegin; WeightsIterator weight = weightsBegin; MaskIterator mask = maskBegin; Int64 count = 0; while (count < nr) { if (*mask && *weight > 0) { _weightedStatsCodeCR } StatisticsIncrementer::increment( datum, count, weight, mask, dataStride, maskStride ); location.second += dataStride; } } } #endif casacore-2.4.1/scimath/Mathematics/ConvolveGridder.h000066400000000000000000000060721321422335000224320ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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-2.4.1/scimath/Mathematics/ConvolveGridder.tcc000066400000000000000000000313601321422335000227520ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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(); 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(); 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-2.4.1/scimath/Mathematics/Convolver.h000066400000000000000000000317251321422335000213160ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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-2.4.1/scimath/Mathematics/Convolver.tcc000066400000000000000000000237251321422335000216410ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (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-2.4.1/scimath/Mathematics/DFTServer.h000066400000000000000000000074551321422335000211500ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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() throw() {} }; // // 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-2.4.1/scimath/Mathematics/DFTServer.tcc000066400000000000000000000232441321422335000214640ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Mathematics/FFTPack.cc000066400000000000000000000172741321422335000207200ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Mathematics/FFTPack.h000066400000000000000000001064321321422335000205550ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 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-2.4.1/scimath/Mathematics/FFTServer.cc000066400000000000000000000030061321422335000212740ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // Instantiate the templates. template class FFTServer; template class FFTServer; } //# NAMESPACE CASACORE - END casacore-2.4.1/scimath/Mathematics/FFTServer.h000066400000000000000000000462221321422335000211450ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef SCIMATH_FFTSERVER_H #define SCIMATH_FFTSERVER_H //# Includes #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations template class Array; template class Matrix; // 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 widely used FORTRAN fftpack // package or the highly optimized FFTW package (to be chosen at build time). //
        // P.N. Swarztrauber, Vectorizing the FFTs, in Parallel Computations // (G. Rodrigue, ed.), Academic Press, 1982, pp. 51--83.
        // The fftpack package only does one dimensional transforms and this class // decomposes multi-dimensional transforms into a series of 1-dimensional ones. //
        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; //# twiddle factors and factorisations used by fftpack PtrBlock *> itsWork; // 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. Do not harm if FFTPack is used. 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-2.4.1/scimath/Mathematics/FFTServer.hcc000066400000000000000000000722411321422335000214530ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: FFTServer.tcc 20253 2008-02-23 15:15:00Z gervandiepen $ #include #include #include #include #include #include #include #include #include #include //# This file contains the templated functions dependent on using FFTW3 //# or FFTPack. //# 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 or FFTPack 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() { for (uInt i=0; i FFTServer & FFTServer:: operator=(const FFTServer & other) { if (this != &other) { for (uInt i=0; i 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; #ifdef HAVE_FFTW3 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= oldSize || itsWork[i] == 0) { itsWork[i] = new Block; } } // Now size the work arrays. There is one for each dimension. // Initialize the FFT. // Only along the first dimension a real <-> complex transform is done, // so it is treated separately. uInt fftLen = fftSize[0]; uInt workSize = 0; uInt bufferLength = itsBuffer.nelements(); switch (transformType) { case FFTEnums::COMPLEX: case FFTEnums::INVCOMPLEX: workSize = 4 * fftLen + 15; bufferLength = std::max(bufferLength, fftLen); break; case FFTEnums::REALTOCOMPLEX: case FFTEnums::COMPLEXTOREAL: workSize = 2 * fftLen + 15; break; case FFTEnums::REALSYMMETRIC: workSize = 3 * fftLen + 15; break; } itsWork[0]->resize (workSize); switch (transformType) { case FFTEnums::COMPLEX: case FFTEnums::INVCOMPLEX: FFTPack::cffti(fftLen, itsWork[0]->storage()); break; case FFTEnums::REALTOCOMPLEX: case FFTEnums::COMPLEXTOREAL: FFTPack::rffti(fftLen, itsWork[0]->storage()); break; case FFTEnums::REALSYMMETRIC: FFTPack::costi(fftLen, itsWork[0]->storage()); break; } // Allocate the work arrays for the other dimensions. for (uInt i=1; iresize (workSize); FFTPack::cffti(fftLen, itsWork[i]->storage()); bufferLength = std::max(bufferLength, fftLen); } // Resize the buffer. itsBuffer.resize (bufferLength, False, False); #endif } } template 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); #ifdef HAVE_FFTW3 IPosition fftwShape(resultShape); objcopy(&(itsWorkIn[0]), dataPtr, itsWorkIn.size()); itsFFTW.r2c(itsSize, &(itsWorkIn[0]), &(itsWorkOut[0])); objcopy(resultPtr, &(itsWorkOut[0]), itsWorkOut.size()); #else // Do real to complex transforms along all the rows { T * workPtr = itsWork[0]->storage(); T * resPtr = (T *) resultPtr; uInt fftLen = shape(0); Bool even = True; if (fftLen%2 == 1) even = False; uInt resultRowLen = resultShape(0)*2; const T * inputRowPtr = dataPtr; T * resultRowPtr = resPtr; const uInt nrows = shape.product()/fftLen; // Iterate over all the rows for (uInt r = 0; r < nrows; ++r) { // Copy data to the complex array objcopy(resultRowPtr, inputRowPtr, fftLen); // Do the Real->Complex row transforms FFTPack::rfftf(fftLen, resultRowPtr, workPtr); // Shuffle elements along if (fftLen > 1) { objmove(resultRowPtr+2, resultRowPtr+1, fftLen-1); } // put zero into imaginary part of the first element *(resultRowPtr+1) = T(0.0); if (even) { // Stick zero into imaginary part of the nyquist sample *(resultRowPtr+resultRowLen-1) = T(0.0); } // Increment the pointers inputRowPtr += fftLen; resultRowPtr += resultRowLen; } } // Do complex to complex transforms along all the remaining axes. const uInt ndim = shape.nelements(); if (ndim > 1) { T * workPtr = 0; S * buffPtr = 0; S * rowPtr = 0; const uInt cElements = resultShape.product(); uInt nffts, r, stride = resultShape(0); for (uInt n = 1; n < ndim; ++n) { uInt fftLen = resultShape(n); nffts = cElements/fftLen; workPtr = itsWork[n]->storage(); buffPtr = itsBuffer.storage(); rowPtr = resultPtr; r = 0; while (r < nffts) { // Copy the data into a temporary buffer. This makes it contiguous and // hence it is more likely to fit into cache. With current computers // this speeds up access to the data by a factors of about ten! objcopy(buffPtr, rowPtr, fftLen, 1u, stride); // Do the transform FFTPack::cfftf(fftLen, buffPtr, workPtr); // copy the data back objcopy(rowPtr, buffPtr, fftLen, stride, 1u); // indexing calculations r++; rowPtr++; if (r%stride == 0) { rowPtr += stride*(fftLen-1); } } stride *= fftLen; } } #endif 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); #ifdef HAVE_FFTW3 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()); #else T * workPtr = 0; // Do complex to complex transforms along all other dimensions const uInt ndim = rShape.nelements(); if (ndim > 1) { S * buffPtr = itsBuffer.storage(); S * rowPtr = 0; const uInt cElements = cShape.product(); uInt n, r, nffts, stride = cShape(0); for (n = 1; n < ndim; ++n) { workPtr = itsWork[n]->storage(); rowPtr = dataPtr; uInt fftLen = rShape(n); nffts = cElements/fftLen; r = 0; while (r < nffts) { // Copy the data into a temporary buffer. This makes it contiguous and // hence it is more likely to fit into cache. With current computers // this speeds up access to the data by a factors of about ten! objcopy(buffPtr, rowPtr, fftLen, 1u, stride); // Do the FFT FFTPack::cfftb(fftLen, buffPtr, workPtr); // copy the data back objcopy(rowPtr, buffPtr, fftLen, stride, 1u); // indexing calculations r++; rowPtr++; if (r%stride == 0) { rowPtr += stride*(fftLen-1); } } stride *= fftLen; } } // Do complex to real transforms along all the rows T * realDataPtr = (T *) dataPtr; workPtr = itsWork[0]->storage(); T * resultRowPtr = resultPtr; const uInt cStride = cShape(0)*2; uInt fftLen = rShape(0); const uInt nffts = rShape.product()/fftLen; // Iterate over all the rows for (uInt r = 0; r < nffts; ++r) { // Copy the data to the real array *resultRowPtr = *realDataPtr; objcopy(resultRowPtr+1, realDataPtr+2, fftLen-1); // Do the Complex->Real row transform FFTPack::rfftb(fftLen, resultRowPtr, workPtr); // Increment the pointers realDataPtr += cStride; resultRowPtr += fftLen; } // While we have a raw pointer handy do the scaling uInt nelem = rResult.nelements(); T scale = T(1)/T(nelem); T * endPtr = resultPtr + nelem; for (resultRowPtr = resultPtr; resultRowPtr < endPtr; resultRowPtr++) { *resultRowPtr *= scale; } #endif 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); #ifdef HAVE_FFTW3 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()); #else const uInt ndim = shape.nelements(); T * workPtr = 0; // Do complex to complex transforms along all the dimensions S * buffPtr = itsBuffer.storage(); T * realBuffPtr = 0; T * endRowPtr = 0; S * rowPtr = 0; const uInt nElements = shape.product(); const T scale = T(1)/T(nElements); const uInt shape0t2 = shape(0) * 2; uInt n, r, nffts, stride = 1u; for (n = 0; n < ndim; ++n) { workPtr = itsWork[n]->storage(); rowPtr = complexPtr; uInt fftLen = shape(n); nffts = nElements/fftLen; r = 0; buffPtr = itsBuffer.storage(); while (r < nffts) { // Copy the data into a temporary buffer. This makes it contiguous and // hence it is more likely to fit into cache. With current computers // this speeds up access to the data by a factors of about ten! if (n != 0) { objcopy(buffPtr, rowPtr, fftLen, 1u, stride); } else { buffPtr = rowPtr; } // Do the FFT if (toFrequency == True) { FFTPack::cfftf(fftLen, buffPtr, workPtr); } else { FFTPack::cfftb(fftLen, buffPtr, workPtr); if (n == 0) { // Scale by 1/N while things are (hopefully) in cache realBuffPtr = (T *) buffPtr; // No need to do complex multiplications when real ones will do. // This saves two multiplies and additions per complex element. for (endRowPtr = realBuffPtr+shape0t2; realBuffPtr < endRowPtr; realBuffPtr++) { *realBuffPtr *= scale; } } } // copy the data back if (n != 0) { objcopy(rowPtr, buffPtr, fftLen, stride, 1u); } // indexing calculations r++; rowPtr++; if (r%stride == 0) { rowPtr += stride*(fftLen-1); } } stride *= fftLen; } #endif 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.*C::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.*C::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.*C::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-2.4.1/scimath/Mathematics/FFTW.cc000066400000000000000000000221731321422335000202420ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include #include #include #include #include #include #include #include #ifdef HAVE_FFTW3 # include #endif #ifdef _OPENMP # include #endif #include namespace casacore { volatile Bool FFTW::is_initialized_fftw = False; 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() : itsPlanR2Cf (0), itsPlanR2C (0), itsPlanC2Rf (0), itsPlanC2R (0), itsPlanC2CFf (0), itsPlanC2CF (0), itsPlanC2CBf (0), itsPlanC2CB (0) { if (!is_initialized_fftw) { ScopedMutexLock lock(theirMutex); if (!is_initialized_fftw) { #ifdef _OPENMP int numCPUs = omp_get_max_threads(); #else int numCPUs = HostInfo::numCPUs(); #endif int nthreads = 1; // cerr << "Number of threads is " << numCPUs << endl; if (numCPUs > 1) { nthreads = numCPUs; } // std::cout << "init threads " << fftwf_init_threads() << std::endl; // std::cout << "init threads " << fftw_init_threads() << std::endl; #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; } } // std::cerr << "will use " << nthreads << " threads " << std::endl; flags = FFTW_ESTIMATE; //flags = FFTW_MEASURE; // std::cerr << "Will FFTW_MEASURE..." << std::endl; //flags = FFTW_PATIENT; std::cerr << "Will FFTW_PATIENT..." << std::endl; //flags = FFTW_EXHAUSTIVE; std::cerr << "Will FFTW_EXHAUSTIVE..." << std::endl; } FFTW::~FFTW() { delete itsPlanR2Cf; delete itsPlanR2C; delete itsPlanC2Rf; delete itsPlanC2R; delete itsPlanC2CFf; delete itsPlanC2CF; delete itsPlanC2CBf; delete itsPlanC2CB; // 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, Complex *out) { delete itsPlanR2Cf; itsPlanR2Cf = new FFTWPlanf (fftwf_plan_dft_r2c(size.nelements(), size.asVector().data(), in, reinterpret_cast(out), flags)); } void FFTW::plan_r2c(const IPosition &size, Double *in, DComplex *out) { delete itsPlanR2C; itsPlanR2C = new FFTWPlan (fftw_plan_dft_r2c(size.nelements(), size.asVector().data(), in, reinterpret_cast(out), flags)); } void FFTW::plan_c2r(const IPosition &size, Complex *in, Float *out) { delete itsPlanC2Rf; itsPlanC2Rf = new FFTWPlanf (fftwf_plan_dft_c2r(size.nelements(), size.asVector().data(), reinterpret_cast(in), out, flags)); } void FFTW::plan_c2r(const IPosition &size, DComplex *in, Double *out) { delete itsPlanC2R; itsPlanC2R = new FFTWPlan (fftw_plan_dft_c2r(size.nelements(), size.asVector().data(), reinterpret_cast(in), out, flags)); } void FFTW::plan_c2c_forward(const IPosition &size, DComplex *in) { delete itsPlanC2CF; itsPlanC2CF = new FFTWPlan (fftw_plan_dft(size.nelements(), size.asVector().data(), reinterpret_cast(in), reinterpret_cast(in), FFTW_FORWARD, flags)); } void FFTW::plan_c2c_forward(const IPosition &size, Complex *in) { delete itsPlanC2CFf; itsPlanC2CFf = new FFTWPlanf (fftwf_plan_dft(size.nelements(), size.asVector().data(), reinterpret_cast(in), reinterpret_cast(in), FFTW_FORWARD, flags)); } void FFTW::plan_c2c_backward(const IPosition &size, DComplex *in) { delete itsPlanC2CB; itsPlanC2CB = new FFTWPlan (fftw_plan_dft(size.nelements(), size.asVector().data(), reinterpret_cast(in), reinterpret_cast(in), FFTW_BACKWARD, flags)); } void FFTW::plan_c2c_backward(const IPosition &size, Complex *in) { delete itsPlanC2CBf; itsPlanC2CBf = new FFTWPlanf (fftwf_plan_dft(size.nelements(), size.asVector().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*, Complex*) { fftwf_execute(itsPlanR2Cf->getPlan()); } void FFTW::r2c(const IPosition&, Double*, DComplex*) { fftw_execute(itsPlanR2C->getPlan()); } void FFTW::c2r(const IPosition&, Complex*, Float*) { fftwf_execute(itsPlanC2Rf->getPlan()); } void FFTW::c2r(const IPosition&, DComplex*, Double*) { fftw_execute(itsPlanC2R->getPlan()); } void FFTW::c2c(const IPosition&, Complex*, Bool forward) { if (forward) { fftwf_execute(itsPlanC2CFf->getPlan()); } else { fftwf_execute(itsPlanC2CBf->getPlan()); } } void FFTW::c2c(const IPosition&, DComplex*, Bool forward) { if (forward) { fftw_execute(itsPlanC2CF->getPlan()); } else { fftw_execute(itsPlanC2CB->getPlan()); } } #else FFTW::FFTW() : itsPlanR2Cf (0), itsPlanR2C (0), itsPlanC2Rf (0), itsPlanC2R (0), itsPlanC2CFf (0), itsPlanC2CF (0), itsPlanC2CBf (0), itsPlanC2CB (0) {} FFTW::~FFTW() {} void FFTW::plan_r2c(const IPosition&, Float*, Complex*) {} void FFTW::plan_r2c(const IPosition&, Double*, DComplex*) {} void FFTW::plan_c2r(const IPosition&, Complex*, Float*) {} void FFTW::plan_c2r(const IPosition&, DComplex*, Double*) {} void FFTW::plan_c2c_forward(const IPosition&, DComplex*) {} void FFTW::plan_c2c_forward(const IPosition&, Complex*) {} void FFTW::plan_c2c_backward(const IPosition&, DComplex*) {} void FFTW::plan_c2c_backward(const IPosition&, Complex*) {} void FFTW::r2c(const IPosition&, Float*, Complex*) {} void FFTW::r2c(const IPosition&, Double*, DComplex*) {} void FFTW::c2r(const IPosition&, Complex*, Float*) {} void FFTW::c2r(const IPosition&, DComplex*, Double*) {} void FFTW::c2c(const IPosition&, Complex*, Bool) {} void FFTW::c2c(const IPosition&, DComplex*, Bool) {} #endif } //# NAMESPACE CASACORE - END casacore-2.4.1/scimath/Mathematics/FFTW.h000066400000000000000000000072361321422335000201070ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef SCIMATH_FFTW_H #define SCIMATH_FFTW_H #include #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() ; // polymorphic interface to fftw[f]_plan... void plan_r2c(const IPosition &size, Float *in, Complex *out) ; void plan_r2c(const IPosition &size, Double *in, DComplex *out) ; void plan_c2r(const IPosition &size, Complex *in, Float *out) ; void plan_c2r(const IPosition &size, DComplex *in, Double *out) ; void plan_c2c_forward(const IPosition &size, DComplex *in) ; void plan_c2c_forward(const IPosition &size, Complex *in) ; void plan_c2c_backward(const IPosition &size, DComplex *in) ; void plan_c2c_backward(const IPosition &size, Complex *in) ; // polymorphic interface to fftw[f]_execute... void r2c(const IPosition &size, Float *in, Complex *out) ; void r2c(const IPosition &size, Double *in, DComplex *out) ; void c2r(const IPosition &size, Complex *in, Float *out); void c2r(const IPosition &size, DComplex *in, Double *out); void c2c(const IPosition &size, Complex *in, Bool forward); void c2c(const IPosition &size, DComplex *in, Bool forward); private: FFTWPlanf* itsPlanR2Cf; FFTWPlan* itsPlanR2C; FFTWPlanf* itsPlanC2Rf; FFTWPlan* itsPlanC2R; FFTWPlanf* itsPlanC2CFf; // forward FFTWPlan* itsPlanC2CF; FFTWPlanf* itsPlanC2CBf; // backward FFTWPlan* itsPlanC2CB; unsigned flags; static volatile Bool is_initialized_fftw; // FFTW needs initialization // only once per process, // not once per object static Mutex theirMutex; // Initialization mutex }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/scimath/Mathematics/FitToHalfStatistics.h000066400000000000000000000256141321422335000232340ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: Array.h 21545 2015-01-22 19:36:35Z gervandiepen $ #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. template 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 ); 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( CountedPtr knownNpts=NULL, CountedPtr knownMin=NULL, CountedPtr knownMax=NULL, uInt binningThreshholdSizeBytes=4096*4096, Bool persistSortedArray=False, uInt64 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 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. AccumType getMedianAndQuantiles( std::map& quantiles, const std::set& fractions, CountedPtr knownNpts=NULL, CountedPtr knownMin=NULL, CountedPtr knownMax=NULL, uInt binningThreshholdSizeBytes=4096*4096, Bool persistSortedArray=False, uInt64 nBins=10000 ); // get the median of the absolute deviation about the median of the data. AccumType getMedianAbsDevMed( CountedPtr knownNpts=NULL, CountedPtr knownMin=NULL, CountedPtr knownMax=NULL, uInt binningThreshholdSizeBytes=4096*4096, Bool persistSortedArray=False, uInt64 nBins=10000 ); // Get the specified quantiles. fractions must be between 0 and 1, // noninclusive. std::map getQuantiles( const std::set& fractions, CountedPtr knownNpts=NULL, CountedPtr knownMin=NULL, CountedPtr knownMax=NULL, uInt binningThreshholdSizeBytes=4096*4096, Bool persistSortedArray=False, uInt64 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. void setStatsToCalculate(std::set& stats); protected: virtual StatsData _getInitialStats() const; 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, Int64 nr, uInt dataStride ); // no weights, no mask void _unweightedStats( StatsData& stats, uInt64& ngood, LocationType& location, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ); void _unweightedStats( StatsData& stats, uInt64& ngood, LocationType& location, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ); void _unweightedStats( StatsData& stats, uInt64& ngood, LocationType& location, const DataIterator& dataBegin, Int64 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, Int64 nr, uInt dataStride ); void _weightedStats( StatsData& stats, LocationType& location, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ); void _weightedStats( StatsData& stats, LocationType& location, const DataIterator& dataBegin, const WeightsIterator& weightBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ); void _weightedStats( StatsData& stats, LocationType& location, const DataIterator& dataBegin, const WeightsIterator& weightBegin, Int64 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, _rangeIsSet; // these are the max and min for the real portion of the dataset CountedPtr _realMax, _realMin; void _getRealMinMax( CountedPtr& realMin, CountedPtr& realMax, CountedPtr knownMin, CountedPtr knownMax ); void _setRange(); }; } #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-2.4.1/scimath/Mathematics/FitToHalfStatistics.tcc000066400000000000000000000544321321422335000235560ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: Array.h 21545 2015-01-22 19:36:35Z gervandiepen $ #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(), _centerType(centerType), _useLower(useData == FitToHalfStatisticsData::LE_CENTER), _centerValue(centerValue), _statsData(initializeStatsData()), _doMedAbsDevMed(False), _rangeIsSet(False), _realMax(), _realMin() { reset(); } 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 = other._realMax.null() ? NULL : new AccumType(*other._realMax); _realMin = other._realMin.null() ? NULL : new AccumType(*other._realMin); return *this; } CASA_STATD StatisticsAlgorithm* FitToHalfStatistics::clone() const { return new FitToHalfStatistics(*this); } CASA_STATD AccumType FitToHalfStatistics::getMedian( CountedPtr , CountedPtr , CountedPtr , uInt , Bool , uInt64 ) { if (_getStatsData().median.null()) { _getStatsData().median = new AccumType(_centerValue); } return *_getStatsData().median; } CASA_STATD AccumType FitToHalfStatistics::getMedianAndQuantiles( std::map& quantileToValue, const std::set& quantiles, CountedPtr knownNpts, CountedPtr knownMin, CountedPtr knownMax, uInt binningThreshholdSizeBytes, Bool persistSortedArray, uInt64 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( CountedPtr knownNpts, CountedPtr knownMin, CountedPtr knownMax, uInt binningThreshholdSizeBytes, Bool persistSortedArray, uInt64 nBins ) { if (_getStatsData().medAbsDevMed.null()) { _setRange(); // 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 CountedPtr realNPts = knownNpts.null() ? new uInt64(getNPts()/2) : new uInt64(*knownNpts/2); CountedPtr realMin, realMax; _getStatsData().medAbsDevMed = new AccumType( ConstrainedRangeStatistics::getMedianAbsDevMed( realNPts, knownMin, knownMax, binningThreshholdSizeBytes, persistSortedArray, nBins ) ); } return *_getStatsData().medAbsDevMed; } CASA_STATD void FitToHalfStatistics::_getRealMinMax( CountedPtr& realMin, CountedPtr& realMax, CountedPtr knownMin, CountedPtr knownMax ) { realMin = new AccumType(_centerValue); realMax = new AccumType(_centerValue); if (knownMin.null() || knownMax.null()) { AccumType mymin, mymax; getMinMax(mymin, mymax); if (_useLower) { realMin = new AccumType(mymin); } else { realMax = new AccumType(mymax); } } else { if (_useLower) { realMin = new AccumType(*knownMin); } else { realMax = new AccumType(*knownMax); } } } CASA_STATD void FitToHalfStatistics::getMinMax( AccumType& mymin, AccumType& mymax ) { if ( _getStatsData().min.null() || _getStatsData().max.null()) { _setRange(); // This call returns the min and max of the real portion of the dataset ConstrainedRangeStatistics::getMinMax(mymin, mymax); _realMin = new AccumType(mymin); _realMax = new AccumType(mymax); if (_useLower) { mymax = TWO*_centerValue - mymin; } else { mymin = TWO*_centerValue - mymax; } _getStatsData().min = new AccumType(mymin); _getStatsData().max = new AccumType(mymax); } else { mymin = *_getStatsData().min; mymax = *_getStatsData().max; } } CASA_STATD std::map FitToHalfStatistics::getQuantiles( const std::set& fractions, CountedPtr knownNpts, CountedPtr knownMin, CountedPtr knownMax, uInt binningThreshholdSizeBytes, Bool persistSortedArray, uInt64 nBins ) { ThrowIf( *fractions.begin() <= 0 || *fractions.rbegin() >= 1, "Value of all quantiles must be between 0 and 1 (noninclusive)" ); ThrowIf ( ! knownNpts.null() && *knownNpts % 2 != 0, "knownNpts must be even for this class" ); _setRange(); // fractions that exist in the virtual part of the dataset are determined from the // real fractions reflected about the center point. std::set realPortionFractions; std::set::const_iterator fiter = fractions.begin(); std::set::const_iterator fend = fractions.end(); // map the actual (full dataset) fractions to the real portion fractions std::map actualToReal; Double freal = 0; std::map actual; for ( ; fiter != fend; ++fiter) { if (near(*fiter, 0.5)) { if (_realMin.null() || _realMax.null()) { AccumType mymin, mymax; getMinMax(mymin, mymax); } actual[*fiter] = _useLower ? *_realMax : TWO*_centerValue - *_realMin; continue; } Bool isVirtualQ = (_useLower && *fiter > 0.5) || (! _useLower && *fiter < 0.5); if (isVirtualQ) { std::set actualF; actualF.insert(*fiter); uInt64 allNPts = knownNpts.null() ? getNPts() : *knownNpts; std::map actualFToI = StatisticsData::indicesFromFractions( allNPts, actualF ); uInt64 actualIdx = actualFToI[*fiter]; uInt64 realIdx = 0; if (_useLower) { realIdx = allNPts - (actualIdx + 1); } else { realIdx = 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 if (_realMax.null()) { AccumType mymin, mymax; getMinMax(mymin, mymax); } actual[*fiter] = TWO*_centerValue - *_realMax; continue; } else if (! _useLower && realIdx == 0) { // the actual index is the reflection of the minimum // value of the real portion ofthe dataset if (_realMin.null()) { AccumType mymin, mymax; getMinMax(mymin, mymax); } actual[*fiter] = TWO*_centerValue - *_realMin; continue; } else { freal = Double(realIdx + 1)/Double(allNPts/2); if (freal == 1) { if (_realMin.null() || _realMax.null()) { AccumType mymin, mymax; getMinMax(mymin, mymax); } actual[*fiter] = *_getStatsData().min; continue; } } } else { // quantile is in the real part of the dataset freal = _useLower ? 2*(*fiter) : 2*(*fiter - 0.5); } realPortionFractions.insert(freal); actualToReal[*fiter] = 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. CountedPtr realNPts = knownNpts.null() ? new uInt64(getNPts()/2) : new uInt64(*knownNpts/2); CountedPtr realMin, realMax; _getRealMinMax(realMin, realMax, knownMin, knownMax); std::map realPart = ConstrainedRangeStatistics::getQuantiles( realPortionFractions, realNPts, realMin, realMax, binningThreshholdSizeBytes, persistSortedArray, nBins ); fiter = fractions.begin(); while (fiter != fend) { if (actual.find(*fiter) == actual.end()) { Double realF = actualToReal[*fiter]; AccumType actualValue = realPart[realF]; if ( (_useLower && *fiter > 0.5) || (! _useLower && *fiter < 0.5) ) { // quantile in virtual part of the data set, reflect corresponding // real value to get actual value actualValue = TWO*_centerValue - actualValue; } actual[*fiter] = actualValue; } ++fiter; } return actual; } CASA_STATD uInt64 FitToHalfStatistics::getNPts() { if (_getStatsData().npts == 0) { _setRange(); // 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; ConstrainedRangeStatistics::reset(); } CASA_STATD void FitToHalfStatistics::setStatsToCalculate( std::set& stats ) { if (! stats.empty() && _centerType == FitToHalfStatisticsData::CMEAN) { stats.insert(StatisticsData::MEAN); } ClassicalStatistics::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(); stats.sum = stats.mean * stats.sumweights; if (_useLower) { stats.maxpos.first = -1; stats.maxpos.second = -1; stats.max = new AccumType(TWO*_centerValue - *stats.min); } else { stats.minpos.first = -1; stats.minpos.second = -1; stats.min = new AccumType(TWO*_centerValue - *stats.max); } return copy(stats); } CASA_STATD void FitToHalfStatistics::_setRange() { if (_rangeIsSet) { return; } ClassicalStatistics cs(*this); if (_centerType == FitToHalfStatisticsData::CMEAN || _centerType == FitToHalfStatisticsData::CMEDIAN) { _centerValue = _centerType == FitToHalfStatisticsData::CMEAN ? cs.getStatistic(StatisticsData::MEAN) : cs.getMedian(); } _getStatsData().mean = _centerValue; _getStatsData().median = new AccumType(_centerValue); AccumType mymin, mymax; cs.getMinMax(mymin, mymax); CountedPtr > range = _useLower ? new std::pair(mymin, _centerValue) : new std::pair(_centerValue, mymax); ConstrainedRangeStatistics::_setRange(range); _rangeIsSet = True; } // use a define to ensure code is compiled inline #define _unweightedStatsCodeFH \ if (this->_isInRange(*datum)) { \ 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, Int64 nr, uInt dataStride ) { DataIterator datum = dataBegin; Int64 count = 0; while (count < nr) { _unweightedStatsCodeFH StatisticsIncrementer::increment( datum, count, True, dataStride ); location.second += dataStride; } } CASA_STATD void FitToHalfStatistics::_unweightedStats( StatsData& stats, uInt64& ngood, LocationType& location, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) { DataIterator datum = dataBegin; Int64 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 ) ) { _unweightedStatsCodeFH } StatisticsIncrementer::increment( datum, count, True, dataStride ); location.second += dataStride; } } CASA_STATD void FitToHalfStatistics::_unweightedStats( StatsData& stats, uInt64& ngood, LocationType& location, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) { DataIterator datum = dataBegin; MaskIterator mask = maskBegin; Int64 count = 0; while (count < nr) { if (*mask) { _unweightedStatsCodeFH } StatisticsIncrementer::increment( datum, count, mask, True, dataStride, maskStride ); location.second += dataStride; } } CASA_STATD void FitToHalfStatistics::_unweightedStats( StatsData& stats, uInt64& ngood, LocationType& location, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) { DataIterator datum = dataBegin; MaskIterator mask = maskBegin; Int64 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 ) ) { _unweightedStatsCodeFH } StatisticsIncrementer::increment( datum, count, mask, True, dataStride, maskStride ); location.second += dataStride; } } CASA_STATD void FitToHalfStatistics::_updateDataProviderMaxMin( const StatsData& threadStats ) { StatsDataProvider *dataProvider = this->_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. StatsData& stats = _getStatsData(); uInt iDataset = this->_getIDataset(); if ( iDataset == threadStats.maxpos.first && (stats.max.null() || *threadStats.max > *stats.max) ) { if (_realMax.null() || *threadStats.max > *_realMax) { _realMax = new AccumType(*threadStats.max); if (! _useLower) { dataProvider->updateMaxPos(threadStats.maxpos); } } } if ( iDataset == threadStats.minpos.first && (stats.min.null() || (*threadStats.min) < (*stats.min)) ) { if (_realMin.null() || (*threadStats.min) < (*_realMin)) { _realMin = new AccumType(*threadStats.min); if (_useLower) { dataProvider->updateMinPos(threadStats.minpos); } } } } // use #define to ensure code is compiled inline #define _weightedStatsCodeFH \ if (this->_isInRange(*datum)) { \ 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, Int64 nr, uInt dataStride ) { DataIterator datum = dataBegin; WeightsIterator weight = weightsBegin; Int64 count = 0; while (count < nr) { if (*weight > 0) { _weightedStatsCodeFH } StatisticsIncrementer::increment( datum, count, weight, True, dataStride ); location.second += dataStride; } } CASA_STATD void FitToHalfStatistics::_weightedStats( StatsData& stats, LocationType& location, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) { DataIterator datum = dataBegin; WeightsIterator weight = weightsBegin; Int64 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 ) ) { _weightedStatsCodeFH } StatisticsIncrementer::increment( datum, count, weight, True, dataStride ); location.second += dataStride; } } CASA_STATD void FitToHalfStatistics::_weightedStats( StatsData& stats, LocationType& location, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) { DataIterator datum = dataBegin; WeightsIterator weight = weightsBegin; MaskIterator mask = maskBegin; Int64 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 ) ) { _weightedStatsCodeFH } StatisticsIncrementer::increment( datum, count, weight, mask, True, dataStride, maskStride ); location.second += dataStride; } } CASA_STATD void FitToHalfStatistics::_weightedStats( StatsData& stats, LocationType& location, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) { DataIterator datum = dataBegin; WeightsIterator weight = weightsBegin; MaskIterator mask = maskBegin; Int64 count = 0; while (count < nr) { if (*mask && *weight > 0) { _weightedStatsCodeFH } StatisticsIncrementer::increment( datum, count, weight, mask, True, dataStride, maskStride ); location.second += dataStride; } } } #endif casacore-2.4.1/scimath/Mathematics/FitToHalfStatisticsData.h000066400000000000000000000033761321422335000240270ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: HostInfoDarwin.h 21521 2014-12-10 08:06:42Z gervandiepen $ #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-2.4.1/scimath/Mathematics/GaussianBeam.cc000066400000000000000000000207111321422335000220270ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: HostInfoDarwin.h 21521 2014-12-10 08:06:42Z gervandiepen $ #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; ThrowIf( majAx.getValue() < 0, "Major axis cannot be less than zero." ); ThrowIf( minAx.getValue() < 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) { 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 = C::pi/(4*C::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-2.4.1/scimath/Mathematics/GaussianBeam.h000066400000000000000000000124031321422335000216700ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: HostInfoDarwin.h 21521 2014-12-10 08:06:42Z gervandiepen $ #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-2.4.1/scimath/Mathematics/Geometry.cc000066400000000000000000000053471321422335000212730ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: Combinatorics.cc 21100 2011-06-28 12:49:00Z gervandiepen $ // #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-2.4.1/scimath/Mathematics/Geometry.h000066400000000000000000000054611321422335000211320ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: Combinatorics.h 21116 2011-07-21 11:23:15Z gervandiepen $ #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-2.4.1/scimath/Mathematics/Gridder.h000066400000000000000000000067151321422335000207220ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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-2.4.1/scimath/Mathematics/Gridder.tcc000066400000000000000000000132341321422335000212360ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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 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. template 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); 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. inline void _accumNpts( uInt64& npts, const DataIterator& dataStart, Int64 nr, uInt dataStride ) const; void _accumNpts( uInt64& npts, const DataIterator& dataStart, Int64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) const; void _accumNpts( uInt64& npts, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) const; void _accumNpts( uInt64& npts, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) const; void _accumNpts( uInt64& npts, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride ) const; void _accumNpts( uInt64& npts, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) const; void _accumNpts( uInt64& npts, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) const; void _accumNpts( uInt64& npts, const DataIterator& dataBegin, const WeightsIterator& weightBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) const; // // virtual void _findBins( vector >& binCounts, vector >& sameVal, vector& allSame, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const vector::BinDesc>& binDesc, const vector& maxLimit ) const; virtual void _findBins( vector >& binCounts, vector >& sameVal, vector& allSame, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude, const vector::BinDesc>& binDesc, const vector& maxLimit ) const; virtual void _findBins( vector >& binCounts, vector >& sameVal, vector& allSame, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const vector::BinDesc>& binDesc, const vector& maxLimit ) const; virtual void _findBins( vector >& binCounts, vector >& sameVal, vector& allSame, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude, const vector::BinDesc>& binDesc, const vector& maxLimit ) const; virtual void _findBins( vector >& binCounts, vector >& sameVal, vector& allSame, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride, const vector::BinDesc>& binDesc, const vector& maxLimit ) const ; virtual void _findBins( vector >& binCounts, vector >& sameVal, vector& allSame, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude, const vector::BinDesc>& binDesc, const vector& maxLimit ) const; virtual void _findBins( vector >& binCounts, vector >& sameVal, vector& allSame, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude, const vector::BinDesc>& binDesc, const vector& maxLimit ) const; virtual void _findBins( vector >& binCounts, vector >& sameVal, vector& allSame, const DataIterator& dataBegin, const WeightsIterator& weightBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const vector::BinDesc>& binDesc, const vector& maxLimit ) const; // // virtual void _minMax( CountedPtr& mymin, CountedPtr& mymax, const DataIterator& dataBegin, Int64 nr, uInt dataStride ) const; virtual void _minMax( CountedPtr& mymin, CountedPtr& mymax, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) const; virtual void _minMax( CountedPtr& mymin, CountedPtr& mymax, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) const; virtual void _minMax( CountedPtr& mymin, CountedPtr& mymax, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) const; virtual void _minMax( CountedPtr& mymin, CountedPtr& mymax, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride ) const; virtual void _minMax( CountedPtr& mymin, CountedPtr& mymax, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) const; virtual void _minMax( CountedPtr& mymin, CountedPtr& mymax, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) const; virtual void _minMax( CountedPtr& mymin, CountedPtr& mymax, const DataIterator& dataBegin, const WeightsIterator& weightBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) 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 void _populateArray( vector& ary, const DataIterator& dataBegin, Int64 nr, uInt dataStride ) const; // ranges void _populateArray( vector& ary, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) const; void _populateArray( vector& ary, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) const; // mask and ranges void _populateArray( vector& ary, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) const; // weights void _populateArray( vector& ary, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride ) const; // weights and ranges void _populateArray( vector& ary, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) const; // weights and mask void _populateArray( vector& ary, const DataIterator& dataBegin, const WeightsIterator& weightBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) const; // weights, mask, ranges void _populateArray( vector& ary, const DataIterator& dataBegin, const WeightsIterator& weightBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) const; // no weights, no mask, no ranges virtual void _populateArrays( vector >& arys, uInt64& currentCount, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const vector > &includeLimits, uInt64 maxCount ) const; // ranges virtual void _populateArrays( vector >& arys, uInt64& currentCount, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude, const vector > &includeLimits, uInt64 maxCount ) const; virtual void _populateArrays( vector >& arys, uInt64& currentCount, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const vector > &includeLimits, uInt64 maxCount ) const; // mask and ranges virtual void _populateArrays( vector >& arys, uInt64& currentCount, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude, const vector > &includeLimits, uInt64 maxCount ) const; // weights virtual void _populateArrays( vector >& arys, uInt64& currentCount, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride, const vector > &includeLimits, uInt64 maxCount ) const; // weights and ranges virtual void _populateArrays( vector >& arys, uInt64& currentCount, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude, const vector > &includeLimits, uInt64 maxCount ) const; // weights and mask virtual void _populateArrays( vector >& arys, uInt64& currentCount, const DataIterator& dataBegin, const WeightsIterator& weightBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const vector > &includeLimits, uInt64 maxCount ) const; // weights, mask, ranges virtual void _populateArrays( vector >& arys, uInt64& currentCount, const DataIterator& dataBegin, const WeightsIterator& weightBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude, const vector > &includeLimits, uInt64 maxCount ) const; // // // no weights, no mask, no ranges Bool _populateTestArray( vector& ary, const DataIterator& dataBegin, Int64 nr, uInt dataStride, uInt maxElements ) const; // ranges Bool _populateTestArray( vector& ary, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude, uInt maxElements ) const; // mask Bool _populateTestArray( vector& ary, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, uInt maxElements ) const; // mask and ranges Bool _populateTestArray( vector& ary, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude, uInt maxElements ) const; // weights Bool _populateTestArray( vector& ary, const DataIterator& dataBegin, const WeightsIterator& weightBegin, Int64 nr, uInt dataStride, uInt maxElements ) const; // weights and ranges Bool _populateTestArray( vector& ary, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude, uInt maxElements ) const; // weights and mask Bool _populateTestArray( vector& ary, const DataIterator& dataBegin, const WeightsIterator& weightBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, uInt maxElements ) const; // weights, mask, ranges Bool _populateTestArray( vector& ary, const DataIterator& dataBegin, const WeightsIterator& weightBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude, uInt maxElements ) const; // // // no weights, no mask, no ranges void _unweightedStats( StatsData& stats, uInt64& ngood, LocationType& location, const DataIterator& dataBegin, Int64 nr, uInt dataStride ); // no weights, no mask void _unweightedStats( StatsData& stats, uInt64& ngood, LocationType& location, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ); void _unweightedStats( StatsData& stats, uInt64& ngood, LocationType& location, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ); void _unweightedStats( StatsData& stats, uInt64& ngood, LocationType& location, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ); // // // has weights, but no mask, no ranges void _weightedStats( StatsData& stats, LocationType& location, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride ); void _weightedStats( StatsData& stats, LocationType& location, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ); void _weightedStats( StatsData& stats, LocationType& location, const DataIterator& dataBegin, const WeightsIterator& weightBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ); void _weightedStats( StatsData& stats, LocationType& location, const DataIterator& dataBegin, const WeightsIterator& weightBegin, Int64 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, _hasRange; void _setRange(); }; } #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-2.4.1/scimath/Mathematics/HingesFencesStatistics.tcc000066400000000000000000001204771321422335000243020ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: Array.h 21545 2015-01-22 19:36:35Z gervandiepen $ #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(), _f(f), _rangeIsSet(False), _hasRange(False) { reset(); } CASA_STATD HingesFencesStatistics::~HingesFencesStatistics() {} CASA_STATD HingesFencesStatistics& HingesFencesStatistics::operator=( const HingesFencesStatistics& other ) { if (this == &other) { return *this; } ClassicalStatistics::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, Int64 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, Int64 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, Int64 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, Int64 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, Int64 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, Int64 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, Int64 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, Int64 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::_findBins( vector >& binCounts, vector >& sameVal, vector& allSame, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const vector::BinDesc>& binDesc, const vector& maxLimit ) const { if (_hasRange) { ConstrainedRangeStatistics::_findBins( binCounts, sameVal, allSame, dataBegin, nr, dataStride, binDesc, maxLimit ); } else { ClassicalStatistics::_findBins( binCounts, sameVal, allSame, dataBegin, nr, dataStride, binDesc, maxLimit ); } } CASA_STATD void HingesFencesStatistics::_findBins( vector >& binCounts, vector >& sameVal, vector& allSame, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude, const vector::BinDesc>& binDesc, const vector& maxLimit ) const { if (_hasRange) { ConstrainedRangeStatistics::_findBins( binCounts, sameVal, allSame, dataBegin, nr, dataStride, ranges, isInclude, binDesc, maxLimit ); } else { ClassicalStatistics::_findBins( binCounts, sameVal, allSame, dataBegin, nr, dataStride, ranges, isInclude, binDesc, maxLimit ); } } CASA_STATD void HingesFencesStatistics::_findBins( vector >& binCounts, vector >& sameVal, vector& allSame, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const vector::BinDesc>& binDesc, const vector& maxLimit ) const { if (_hasRange) { ConstrainedRangeStatistics::_findBins( binCounts, sameVal, allSame, dataBegin, nr, dataStride, maskBegin, maskStride, binDesc, maxLimit ); } else { ClassicalStatistics::_findBins( binCounts, sameVal, allSame, dataBegin, nr, dataStride, maskBegin, maskStride, binDesc, maxLimit ); } } CASA_STATD void HingesFencesStatistics::_findBins( vector >& binCounts, vector >& sameVal, vector& allSame, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude, const vector::BinDesc>& binDesc, const vector& maxLimit ) const { if (_hasRange) { ConstrainedRangeStatistics::_findBins( binCounts, sameVal, allSame, dataBegin, nr, dataStride, maskBegin, maskStride, ranges, isInclude, binDesc, maxLimit ); } else { ClassicalStatistics::_findBins( binCounts, sameVal, allSame, dataBegin, nr, dataStride, maskBegin, maskStride, ranges, isInclude, binDesc, maxLimit ); } } CASA_STATD void HingesFencesStatistics::_findBins( vector >& binCounts, vector >& sameVal, vector& allSame, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride, const vector::BinDesc>& binDesc, const vector& maxLimit ) const { if (_hasRange) { ConstrainedRangeStatistics::_findBins( binCounts, sameVal, allSame, dataBegin, weightsBegin, nr, dataStride, binDesc, maxLimit ); } else { ClassicalStatistics::_findBins( binCounts, sameVal, allSame, dataBegin, weightsBegin, nr, dataStride, binDesc, maxLimit ); } } CASA_STATD void HingesFencesStatistics::_findBins( vector >& binCounts, vector >& sameVal, vector& allSame, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude, const vector::BinDesc>& binDesc, const vector& maxLimit ) const { if (_hasRange) { ConstrainedRangeStatistics::_findBins( binCounts, sameVal, allSame, dataBegin, weightsBegin, nr, dataStride, ranges, isInclude, binDesc, maxLimit ); } else { ClassicalStatistics::_findBins( binCounts, sameVal, allSame, dataBegin, weightsBegin, nr, dataStride, ranges, isInclude, binDesc, maxLimit ); } } CASA_STATD void HingesFencesStatistics::_findBins( vector >& binCounts, vector >& sameVal, vector& allSame, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude, const vector::BinDesc>& binDesc, const vector& maxLimit ) const { if (_hasRange) { ConstrainedRangeStatistics::_findBins( binCounts, sameVal, allSame, dataBegin, weightsBegin, nr, dataStride, maskBegin, maskStride, ranges, isInclude, binDesc, maxLimit ); } else { ClassicalStatistics::_findBins( binCounts, sameVal, allSame, dataBegin, weightsBegin, nr, dataStride, maskBegin, maskStride, ranges, isInclude, binDesc, maxLimit ); } } CASA_STATD void HingesFencesStatistics::_findBins( vector >& binCounts, vector >& sameVal, vector& allSame, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const vector::BinDesc>& binDesc, const vector& maxLimit ) const { if (_hasRange) { ConstrainedRangeStatistics::_findBins( binCounts, sameVal, allSame, dataBegin, weightsBegin, nr, dataStride, maskBegin, maskStride, binDesc, maxLimit ); } else { ClassicalStatistics::_findBins( binCounts, sameVal, allSame, dataBegin, weightsBegin, nr, dataStride, maskBegin, maskStride, binDesc, maxLimit ); } } CASA_STATD void HingesFencesStatistics::_minMax( CountedPtr& mymin, CountedPtr& mymax, const DataIterator& dataBegin, Int64 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( CountedPtr& mymin, CountedPtr& mymax, const DataIterator& dataBegin, Int64 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( CountedPtr& mymin, CountedPtr& mymax, const DataIterator& dataBegin, Int64 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( CountedPtr& mymin, CountedPtr& mymax, const DataIterator& dataBegin, Int64 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( CountedPtr& mymin, CountedPtr& mymax, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 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( CountedPtr& mymin, CountedPtr& mymax, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 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( CountedPtr& mymin, CountedPtr& mymax, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 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( CountedPtr& mymin, CountedPtr& mymax, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 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::_populateArray( vector& ary, const DataIterator& dataBegin, Int64 nr, uInt dataStride ) const { if (_hasRange) { ConstrainedRangeStatistics::_populateArray( ary, dataBegin, nr, dataStride ); } else { ClassicalStatistics::_populateArray( ary, dataBegin, nr, dataStride ); } } CASA_STATD void HingesFencesStatistics::_populateArray( vector& ary, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) const { if (_hasRange) { ConstrainedRangeStatistics::_populateArray( ary, dataBegin, nr, dataStride, ranges, isInclude ); } else { ClassicalStatistics::_populateArray( ary, dataBegin, nr, dataStride, ranges, isInclude ); } } CASA_STATD void HingesFencesStatistics::_populateArray( vector& ary, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) const { if (_hasRange) { ConstrainedRangeStatistics::_populateArray( ary, dataBegin, nr, dataStride, maskBegin, maskStride ); } else { ClassicalStatistics::_populateArray( ary, dataBegin, nr, dataStride, maskBegin, maskStride ); } } CASA_STATD void HingesFencesStatistics::_populateArray( vector& ary, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) const { if (_hasRange) { ConstrainedRangeStatistics::_populateArray( ary, dataBegin, nr, dataStride, maskBegin, maskStride, ranges, isInclude ); } else { ClassicalStatistics::_populateArray( ary, dataBegin, nr, dataStride, maskBegin, maskStride, ranges, isInclude ); } } CASA_STATD void HingesFencesStatistics::_populateArray( vector& ary, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride ) const { if (_hasRange) { ConstrainedRangeStatistics::_populateArray( ary, dataBegin, weightsBegin, nr, dataStride ); } else { ClassicalStatistics::_populateArray( ary, dataBegin, weightsBegin, nr, dataStride ); } } CASA_STATD void HingesFencesStatistics::_populateArray( vector& ary, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) const { if (_hasRange) { ConstrainedRangeStatistics::_populateArray( ary, dataBegin, weightsBegin, nr, dataStride, ranges, isInclude ); } else { ClassicalStatistics::_populateArray( ary, dataBegin, weightsBegin, nr, dataStride, ranges, isInclude ); } } CASA_STATD void HingesFencesStatistics::_populateArray( vector& ary, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) const { if (_hasRange) { ConstrainedRangeStatistics::_populateArray( ary, dataBegin, weightsBegin, nr, dataStride, maskBegin, maskStride ); } else { ClassicalStatistics::_populateArray( ary, dataBegin, weightsBegin, nr, dataStride, maskBegin, maskStride ); } } CASA_STATD void HingesFencesStatistics::_populateArray( vector& ary, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) const { if (_hasRange) { ConstrainedRangeStatistics::_populateArray( ary, dataBegin, weightsBegin, nr, dataStride, maskBegin, maskStride, ranges, isInclude ); } else { ClassicalStatistics::_populateArray( ary, dataBegin, weightsBegin, nr, dataStride, maskBegin, maskStride, ranges, isInclude ); } } CASA_STATD void HingesFencesStatistics::_populateArrays( vector >& arys, uInt64& currentCount, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const vector > &includeLimits, uInt64 maxCount ) const { if (_hasRange) { ConstrainedRangeStatistics::_populateArrays( arys, currentCount, dataBegin, nr, dataStride, includeLimits, maxCount ); } else { ClassicalStatistics::_populateArrays( arys, currentCount, dataBegin, nr, dataStride, includeLimits, maxCount ); } } CASA_STATD void HingesFencesStatistics::_populateArrays( vector >& arys, uInt64& currentCount, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude, const vector > &includeLimits, uInt64 maxCount ) const { if (_hasRange) { ConstrainedRangeStatistics::_populateArrays( arys, currentCount, dataBegin, nr, dataStride, ranges, isInclude, includeLimits, maxCount ); } else { ClassicalStatistics::_populateArrays( arys, currentCount, dataBegin, nr, dataStride, ranges, isInclude, includeLimits, maxCount ); } } CASA_STATD void HingesFencesStatistics::_populateArrays( vector >& arys, uInt64& currentCount, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const vector > &includeLimits, uInt64 maxCount ) const { if (_hasRange) { ConstrainedRangeStatistics::_populateArrays( arys, currentCount, dataBegin, nr, dataStride, maskBegin, maskStride, includeLimits, maxCount ); } else { ClassicalStatistics::_populateArrays( arys, currentCount, dataBegin, nr, dataStride, maskBegin, maskStride, includeLimits, maxCount ); } } CASA_STATD void HingesFencesStatistics::_populateArrays( vector >& arys, uInt64& currentCount, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude, const vector > &includeLimits, uInt64 maxCount ) const { if (_hasRange) { ConstrainedRangeStatistics::_populateArrays( arys, currentCount, dataBegin, nr, dataStride, maskBegin, maskStride, ranges, isInclude, includeLimits, maxCount ); } else { ClassicalStatistics::_populateArrays( arys, currentCount, dataBegin, nr, dataStride, maskBegin, maskStride, ranges, isInclude, includeLimits, maxCount ); } } CASA_STATD void HingesFencesStatistics::_populateArrays( vector >& arys, uInt64& currentCount, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride, const vector > &includeLimits, uInt64 maxCount ) const { if (_hasRange) { ConstrainedRangeStatistics::_populateArrays( arys, currentCount, dataBegin, weightsBegin, nr, dataStride, includeLimits, maxCount ); } else { ClassicalStatistics::_populateArrays( arys, currentCount, dataBegin, weightsBegin, nr, dataStride, includeLimits, maxCount ); } } CASA_STATD void HingesFencesStatistics::_populateArrays( vector >& arys, uInt64& currentCount, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude, const vector > &includeLimits, uInt64 maxCount ) const { if (_hasRange) { ConstrainedRangeStatistics::_populateArrays( arys, currentCount, dataBegin, weightsBegin, nr, dataStride, ranges, isInclude, includeLimits, maxCount ); } else { ClassicalStatistics::_populateArrays( arys, currentCount, dataBegin, weightsBegin, nr, dataStride, ranges, isInclude, includeLimits, maxCount ); } } CASA_STATD void HingesFencesStatistics::_populateArrays( vector >& arys, uInt64& currentCount, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const vector > &includeLimits, uInt64 maxCount ) const { if (_hasRange) { ConstrainedRangeStatistics::_populateArrays( arys, currentCount, dataBegin, weightsBegin, nr, dataStride, maskBegin, maskStride, includeLimits, maxCount ); } else { ClassicalStatistics::_populateArrays( arys, currentCount, dataBegin, weightsBegin, nr, dataStride, maskBegin, maskStride, includeLimits, maxCount ); } } CASA_STATD void HingesFencesStatistics::_populateArrays( vector >& arys, uInt64& currentCount, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude, const vector > &includeLimits, uInt64 maxCount ) const { if (_hasRange) { ConstrainedRangeStatistics::_populateArrays( arys, currentCount, dataBegin, weightsBegin, nr, dataStride, maskBegin, maskStride, ranges, isInclude, includeLimits, maxCount ); } else { ClassicalStatistics::_populateArrays( arys, currentCount, dataBegin, weightsBegin, nr, dataStride, maskBegin, maskStride, ranges, isInclude, includeLimits, maxCount ); } } CASA_STATD Bool HingesFencesStatistics::_populateTestArray( vector& ary, const DataIterator& dataBegin, Int64 nr, uInt dataStride, uInt maxElements ) const { if (_hasRange) { return ConstrainedRangeStatistics::_populateTestArray( ary, dataBegin, nr, dataStride, maxElements ); } else { return ClassicalStatistics::_populateTestArray( ary, dataBegin, nr, dataStride, maxElements ); } } CASA_STATD Bool HingesFencesStatistics::_populateTestArray( vector& ary, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude, uInt maxElements ) const { if (_hasRange) { return ConstrainedRangeStatistics::_populateTestArray( ary, dataBegin, nr, dataStride, ranges, isInclude, maxElements ); } else { return ClassicalStatistics::_populateTestArray( ary, dataBegin, nr, dataStride, ranges, isInclude, maxElements ); } } CASA_STATD Bool HingesFencesStatistics::_populateTestArray( vector& ary, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, uInt maxElements ) const { if (_hasRange) { return ConstrainedRangeStatistics::_populateTestArray( ary, dataBegin, nr, dataStride, maskBegin, maskStride, maxElements ); } else { return ClassicalStatistics::_populateTestArray( ary, dataBegin, nr, dataStride, maskBegin, maskStride, maxElements ); } } CASA_STATD Bool HingesFencesStatistics::_populateTestArray( vector& ary, const DataIterator& dataBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude, uInt maxElements ) const { if (_hasRange) { return ConstrainedRangeStatistics::_populateTestArray( ary, dataBegin, nr, dataStride, maskBegin, maskStride, ranges, isInclude, maxElements ); } else { return ClassicalStatistics::_populateTestArray( ary, dataBegin, nr, dataStride, maskBegin, maskStride, ranges, isInclude, maxElements ); } } CASA_STATD Bool HingesFencesStatistics::_populateTestArray( vector& ary, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride, uInt maxElements ) const { if (_hasRange) { return ConstrainedRangeStatistics::_populateTestArray( ary, dataBegin, weightsBegin, nr, dataStride, maxElements ); } else { return ClassicalStatistics::_populateTestArray( ary, dataBegin, weightsBegin, nr, dataStride, maxElements ); } } CASA_STATD Bool HingesFencesStatistics::_populateTestArray( vector& ary, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude, uInt maxElements ) const { if (_hasRange) { return ConstrainedRangeStatistics::_populateTestArray( ary, dataBegin, weightsBegin, nr, dataStride, ranges, isInclude, maxElements ); } else { return ClassicalStatistics::_populateTestArray( ary, dataBegin, weightsBegin, nr, dataStride, ranges, isInclude, maxElements ); } } CASA_STATD Bool HingesFencesStatistics::_populateTestArray( vector& ary, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, uInt maxElements ) const { if (_hasRange) { return ConstrainedRangeStatistics::_populateTestArray( ary, dataBegin, weightsBegin, nr, dataStride, maskBegin, maskStride, maxElements ); } else { return ClassicalStatistics::_populateTestArray( ary, dataBegin, weightsBegin, nr, dataStride, maskBegin, maskStride, maxElements ); } } CASA_STATD Bool HingesFencesStatistics::_populateTestArray( vector& ary, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, Int64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude, uInt maxElements ) const { if (_hasRange) { return ConstrainedRangeStatistics::_populateTestArray( ary, dataBegin, weightsBegin, nr, dataStride, maskBegin, maskStride, ranges, isInclude, maxElements ); } else { return ClassicalStatistics::_populateTestArray( ary, dataBegin, weightsBegin, nr, dataStride, maskBegin, maskStride, ranges, isInclude, maxElements ); } } CASA_STATD void HingesFencesStatistics::_setRange() { if (_rangeIsSet) { return; } if (_f < 0) { _rangeIsSet = True; _hasRange = False; return; } std::set quantiles; quantiles.insert(0.25); quantiles.insert(0.75); ClassicalStatistics cs(*this); std::map quartiles = cs.getQuantiles(quantiles); //ClassicalStatistics::_clearStats(); AccumType iqr = quartiles[0.75] - quartiles[0.25]; CountedPtr > range = new std::pair( quartiles[0.25] - _f*iqr, quartiles[0.75] + _f*iqr ); ConstrainedRangeStatistics::_setRange(range); _rangeIsSet = True; _hasRange = True; } CASA_STATD void HingesFencesStatistics::_unweightedStats( StatsData& stats, uInt64& ngood, LocationType& location, const DataIterator& dataBegin, Int64 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, Int64 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, Int64 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, Int64 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, Int64 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, Int64 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, Int64 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, Int64 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-2.4.1/scimath/Mathematics/HistAcc.h000066400000000000000000000202651321422335000206540ustar00rootroot00000000000000//# HistAcc.h: Histogram 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #ifndef SCIMATH_HISTACC_H #define SCIMATH_HISTACC_H #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // forward declarations: template class Array; 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-2.4.1/scimath/Mathematics/HistAcc.tcc000066400000000000000000000362651321422335000212050ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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 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); } // 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-2.4.1/scimath/Mathematics/Interpolate2D.h000066400000000000000000000204161321422335000220100ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef SCIMATH_INTERPOLATE2D_H #define SCIMATH_INTERPOLATE2D_H //# Includes #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward declarations template class Vector; template class Matrix; 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 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-2.4.1/scimath/Mathematics/Interpolate2D2.tcc000066400000000000000000000220401321422335000224070ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 = floor(x); const T floory = 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(C::pi * x) / (C::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-2.4.1/scimath/Mathematics/InterpolateArray1D.h000066400000000000000000000262711321422335000230130ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: Interpolate1DArray.h,v 8.1 1997/05/21 22:59:29 rm #ifndef SCIMATH_INTERPOLATEARRAY1D_H #define SCIMATH_INTERPOLATEARRAY1D_H #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template class PtrBlock; template class Block; template class Array; template class Vector; template class Cube; // 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-2.4.1/scimath/Mathematics/InterpolateArray1D.tcc000066400000000000000000000707531321422335000233410ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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); Vector vxin(xin); 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); Vector vxin(xin); 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() throw() {} }; // // 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-2.4.1/scimath/Mathematics/MathFunc.tcc000066400000000000000000000417571321422335000213760ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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*C::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*C::ln2) { // nothing } // // copy constructor // template GaussianConv::GaussianConv(const GaussianConv& other): MathFunc(), sup_width(other.sup_width), fw2(other.fw2), ln16(4.0*C::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 = C::pi * kbparm; T x2 = C::pi * sqrt(par2 - 1.0); T x3 = C::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 * C::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*C::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*C::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 = C::pi * kbparm; T x2 = C::pi * sqrt(par2 - 1.0); T x3 = C::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 * C::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 = C::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/C::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 * C::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-2.4.1/scimath/Mathematics/MathFunc2.cc000066400000000000000000000421461321422335000212650ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Mathematics/MatrixMathLA.h000066400000000000000000000223741321422335000216340ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Mathematics/MatrixMathLA.tcc000066400000000000000000000141471321422335000221550ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef SCIMATH_MATRIXMATHLA_TCC #define SCIMATH_MATRIXMATHLA_TCC #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-2.4.1/scimath/Mathematics/MatrixSolver.cc000066400000000000000000000110461321422335000221300ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Mathematics/MedianSlider.cc000066400000000000000000000133601321422335000220320ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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( abs(c1-c2) > 1 ) { throw(AipsError("MedianSlider::assure() failed")); return False; } return True; } } //# NAMESPACE CASACORE - END casacore-2.4.1/scimath/Mathematics/MedianSlider.h000066400000000000000000000107551321422335000217010ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Mathematics/NNGridder.h000066400000000000000000000046071321422335000211540ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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-2.4.1/scimath/Mathematics/NNGridder.tcc000066400000000000000000000055661321422335000215030ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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-2.4.1/scimath/Mathematics/NNLSMatrixSolver.cc000066400000000000000000000074561321422335000226350ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Mathematics/NNLSMatrixSolver.h000066400000000000000000000061551321422335000224720ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Mathematics/NumericTraits.cc000066400000000000000000000041301321422335000222560ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN const Double & NumericTraits::epsilon = C::flt_epsilon; const Double & NumericTraits::epsilon = C::dbl_epsilon; const Double & NumericTraits::epsilon = C::flt_epsilon; const Double & NumericTraits::epsilon = C::dbl_epsilon; const Double & NumericTraits::minimum = C::flt_min; const Double & NumericTraits::minimum = C::dbl_min; const Double & NumericTraits::minimum = C::flt_min; const Double & NumericTraits::minimum = C::dbl_min; const Double & NumericTraits::maximum = C::flt_max; const Double & NumericTraits::maximum = C::dbl_max; const Double & NumericTraits::maximum = C::flt_max; const Double & NumericTraits::maximum = C::dbl_max; } //# NAMESPACE CASACORE - END casacore-2.4.1/scimath/Mathematics/NumericTraits.h000066400000000000000000000343551321422335000221340ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Mathematics/NumericTraits2.h000066400000000000000000000064561321422335000222170ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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-2.4.1/scimath/Mathematics/RigidVector.h000066400000000000000000000230271321422335000215560ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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-2.4.1/scimath/Mathematics/RigidVector.tcc000066400000000000000000000067051321422335000221040ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Mathematics/RigidVector2.cc000066400000000000000000000046261321422335000220020ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Mathematics/SCSL.cc000066400000000000000000000357751321422335000202540ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Mathematics/SCSL.h000066400000000000000000002517701321422335000201110ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Mathematics/Smooth.h000066400000000000000000000064101321422335000206030ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Mathematics/Smooth.tcc000066400000000000000000000113401321422335000211230ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef SCIMATH_SMOOTH_TCC #define SCIMATH_SMOOTH_TCC // #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-2.4.1/scimath/Mathematics/SparseDiff.h000066400000000000000000000363061321422335000213670ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id: SparseDiff.h,v 1.3 2008/01/10 12:00:42 wbrouw Exp $ #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-2.4.1/scimath/Mathematics/SparseDiff.tcc000066400000000000000000000240241321422335000217030ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: SparseDiff.cc,v 1.3 2008/01/10 12:00:42 wbrouw Exp $ #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-2.4.1/scimath/Mathematics/SparseDiffA.h000066400000000000000000000132351321422335000214640ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id: SparseDiffA.h,v 1.1 2007/11/16 04:34:46 wbrouw Exp $ #ifndef SCIMATH_SPARSEDIFFA_H #define SCIMATH_SPARSEDIFFA_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. // // // // 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-2.4.1/scimath/Mathematics/SparseDiffIO.h000066400000000000000000000044511321422335000216130ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: SparseDiffIO.h,v 1.1 2007/11/16 04:34:46 wbrouw Exp $ #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-2.4.1/scimath/Mathematics/SparseDiffIO.tcc000066400000000000000000000036101321422335000221310ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: SparseDiffIO.cc,v 1.1 2007/11/16 04:34:46 wbrouw Exp $ #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-2.4.1/scimath/Mathematics/SparseDiffMath.tcc000066400000000000000000000401011321422335000225070ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: SparseDiffMath.cc,v 1.1 2007/11/16 04:34:46 wbrouw Exp $ #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(C::_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(-C::_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-2.4.1/scimath/Mathematics/SparseDiffRep.h000066400000000000000000000101451321422335000220270ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id: SparseDiffRep.h,v 1.1 2007/11/16 04:34:46 wbrouw Exp $ #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-2.4.1/scimath/Mathematics/SparseDiffRep.tcc000066400000000000000000000050021321422335000223450ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: SparseDiffRep.cc,v 1.1 2007/11/16 04:34:46 wbrouw Exp $ #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-2.4.1/scimath/Mathematics/SparseDiffX.h000066400000000000000000000132361321422335000215140ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id: SparseDiffX.h,v 1.1 2007/11/16 04:34:46 wbrouw Exp $ #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-2.4.1/scimath/Mathematics/SquareMatrix.h000066400000000000000000000303431321422335000217610ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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-2.4.1/scimath/Mathematics/SquareMatrix.tcc000066400000000000000000000265141321422335000223100ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Mathematics/SquareMatrix2.cc000066400000000000000000000105211321422335000221750ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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; } } 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; } } 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-2.4.1/scimath/Mathematics/StatAcc.h000066400000000000000000000167721321422335000206700ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #ifndef SCIMATH_STATACC_H #define SCIMATH_STATACC_H #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // forward declarations: template class Array; 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-2.4.1/scimath/Mathematics/StatAcc.tcc000066400000000000000000000232611321422335000212010ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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-2.4.1/scimath/Mathematics/StatisticsAlgorithm.h000066400000000000000000000413211321422335000233330ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: Array.h 21545 2015-01-22 19:36:35Z gervandiepen $ #ifndef SCIMATH_STATSALGORITHM_H #define SCIMATH_STATSALGORITHM_H #include #include #include #include #include #include #include #include #include // because the template signature has become unwieldy #define CASA_STATD template #define CASA_STATP AccumType, DataIterator, MaskIterator, WeightsIterator namespace casacore { // Base class of statistics algorithm class hierarchy. // The default implementation is such that statistics are only calculated when // getStatistic() or getStatistics() is 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() subsequently to 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 a data provider. This takes the form of // a derived class of StatsDataProvider, in which various methods are implemented for // retrieving various information about the data sets. Such an interface is necessary for // data which does not easily lend itself 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 in this way), one must the // LatticeStatsDataProvider class, which the statistics framework will use to iteratate // through the lattice, only keeping one chunk of the data of the lattice in memory at one // time. // // 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 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 correspondance 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 indeces 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 quantile value must exist in the // dataset. 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 pruposes of // quantile calculations. template 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. virtual void addData( const DataIterator& first, uInt nr, uInt dataStride=1, Bool nrAccountsForStride=False ); virtual void addData( const DataIterator& first, uInt nr, const DataRanges& dataRanges, Bool isInclude=True, uInt dataStride=1, Bool nrAccountsForStride=False ); virtual void addData( const DataIterator& first, const MaskIterator& maskFirst, uInt nr, uInt dataStride=1, Bool nrAccountsForStride=False, uInt maskStride=1 ); virtual 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 ); virtual void addData( const DataIterator& first, const WeightsIterator& weightFirst, uInt nr, uInt dataStride=1, Bool nrAccountsForStride=False ); virtual void addData( const DataIterator& first, const WeightsIterator& weightFirst, uInt nr, const DataRanges& dataRanges, Bool isInclude=True, uInt dataStride=1, Bool nrAccountsForStride=False ); virtual void addData( const DataIterator& first, const WeightsIterator& weightFirst, const MaskIterator& maskFirst, uInt nr, uInt dataStride=1, Bool nrAccountsForStride=False, uInt maskStride=1 ); virtual 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; // delete any (partially) sorted array void deleteSortedArray(); virtual AccumType getMedian( CountedPtr knownNpts=NULL, CountedPtr knownMin=NULL, CountedPtr knownMax=NULL, uInt binningThreshholdSizeBytes=4096*4096, Bool persistSortedArray=False, uInt64 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, CountedPtr knownNpts=NULL, CountedPtr knownMin=NULL, CountedPtr knownMax=NULL, uInt binningThreshholdSizeBytes=4096*4096, Bool persistSortedArray=False, uInt64 nBins=10000 ) = 0; // get the median of the absolute deviation about the median of the data. virtual AccumType getMedianAbsDevMed( CountedPtr knownNpts=NULL, CountedPtr knownMin=NULL, CountedPtr knownMax=NULL, uInt binningThreshholdSizeBytes=4096*4096, Bool persistSortedArray=False, uInt64 nBins=10000 ) = 0; AccumType getQuantile( Double quantile, CountedPtr knownNpts=NULL, CountedPtr knownMin=NULL, CountedPtr knownMax=NULL, uInt binningThreshholdSizeBytes=4096*4096, Bool persistSortedArray=False, uInt64 nBins=10000 ); // get a map of quantiles to values. virtual std::map getQuantiles( const std::set& quantiles, CountedPtr npts=NULL, CountedPtr min=NULL, CountedPtr max=NULL, uInt binningThreshholdSizeBytes=4096*4096, Bool persistSortedArray=False, uInt64 nBins=10000 ) = 0; // get the value of the specified statistic virtual 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 std::pair getStatisticIndex(StatisticsData::STATS stat) = 0; virtual 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. virtual void setData(const DataIterator& first, uInt nr, uInt dataStride=1, Bool nrAccountsForStride=False); virtual void setData( const DataIterator& first, uInt nr, const DataRanges& dataRanges, Bool isInclude=True, uInt dataStride=1, Bool nrAccountsForStride=False ); virtual void setData( const DataIterator& first, const MaskIterator& maskFirst, uInt nr, uInt dataStride=1, Bool nrAccountsForStride=False, uInt maskStride=1 ); virtual 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 ); virtual void setData( const DataIterator& first, const WeightsIterator& weightFirst, uInt nr, uInt dataStride=1, Bool nrAccountsForStride=False ); virtual void setData( const DataIterator& first, const WeightsIterator& weightFirst, uInt nr, const DataRanges& dataRanges, Bool isInclude=True, uInt dataStride=1, Bool nrAccountsForStride=False ); virtual void setData( const DataIterator& first, const WeightsIterator& weightFirst, const MaskIterator& maskFirst, uInt nr, uInt dataStride=1, Bool nrAccountsForStride=False, uInt maskStride=1 ); virtual 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 settng 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. 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 StatisticsAlgorithm& operator=( const StatisticsAlgorithm& other ); // Allows derived classes to do things after data is set or added. // Default implementation does nothing. virtual void _addData() {} const vector& _getCounts() const { return _counts; } const vector& _getData() const { return _data; } StatsDataProvider* _getDataProvider() { return _dataProvider; } const StatsDataProvider* _getDataProvider() const { return _dataProvider; } const vector& _getDataStrides() const { return _dataStrides; } const std::map& _getIsIncludeRanges() const { return _isIncludeRanges; } const std::map _getMasks() const { return _masks; } const std::map& _getMaskStrides() const { return _maskStrides; } const std::map& _getRanges() const { return _dataRanges; } virtual AccumType _getStatistic(StatisticsData::STATS stat) = 0; virtual StatsData _getStatistics() = 0; const std::set _getStatsToCalculate() const { return _statsToCalculate; } std::vector& _getSortedArray() { return _sortedArray; } virtual const std::set& _getUnsupportedStatistics() const { return _unsupportedStats; } const std::map& _getWeights() const { return _weights; } /* // get the zero-based indices of the specified quantiles in sorted dataset with npts // number of good points. The returned map maps quantiles to indices. static std::map _indicesFromQuantiles( uInt64 npts, const std::set& quantiles ); */ // The array can be changed by paritally sorting it up to the largest index. Return // a map of index to value in the sorted array. static std::map _valuesFromArray( vector& myArray, const std::set& indices ); void _setSortedArray(const vector& v) { _sortedArray = v; } private: vector _data; // maps data to weights std::map _weights; // maps data to masks std::map _masks; vector _counts; vector _dataStrides; std::map _maskStrides; std::map _isIncludeRanges; std::map _dataRanges; vector _sortedArray; std::set _statsToCalculate, _unsupportedStats; StatsDataProvider *_dataProvider; void _throwIfDataProviderDefined() const; }; } #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif #endif casacore-2.4.1/scimath/Mathematics/StatisticsAlgorithm.tcc000066400000000000000000000277551321422335000236740ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: Array.h 21545 2015-01-22 19:36:35Z gervandiepen $ #ifndef SCIMATH_STATISTICSALGORITHM_TCC #define SCIMATH_STATISTICSALGORITHM_TCC #include #include #include namespace casacore { CASA_STATD StatisticsAlgorithm::StatisticsAlgorithm() : _data(), _weights(), _masks(), _counts(), _dataStrides(), _maskStrides(), _isIncludeRanges(), _dataRanges(), _sortedArray(), _statsToCalculate(), _unsupportedStats(), _dataProvider(NULL) {} CASA_STATD StatisticsAlgorithm& StatisticsAlgorithm::operator= ( const StatisticsAlgorithm& 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; _sortedArray = other._sortedArray; _statsToCalculate = other._statsToCalculate; _unsupportedStats = other._unsupportedStats; // WARN reference sementics _dataProvider = other._dataProvider; return *this; } CASA_STATD StatisticsAlgorithm::~StatisticsAlgorithm() {} CASA_STATD void StatisticsAlgorithm::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); _addData(); } CASA_STATD void StatisticsAlgorithm::addData( const DataIterator& first, uInt nr, const DataRanges& dataRanges, Bool isInclude, uInt dataStride, Bool nrAccountsForStride ) { _throwIfDataProviderDefined(); typename DataRanges::const_iterator riter = dataRanges.begin(); typename DataRanges::const_iterator rend = dataRanges.end(); while (riter != rend) { ThrowIf( (*riter).first > (*riter).second, "The first value in a range pair cannot be greater than the second" ); ++riter; } uInt n = _data.size(); _isIncludeRanges[n] = isInclude; _dataRanges[n] = dataRanges; this->addData(first, nr, dataStride, nrAccountsForStride); } CASA_STATD void StatisticsAlgorithm::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; this->addData(first, nr, dataStride, nrAccountsForStride); } CASA_STATD void StatisticsAlgorithm::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; this->addData( first, nr, dataRanges, isInclude, dataStride, nrAccountsForStride ); } CASA_STATD void StatisticsAlgorithm::addData( const DataIterator& first, const WeightsIterator& weightFirst, uInt nr, uInt dataStride, Bool nrAccountsForStride ) { _throwIfDataProviderDefined(); _weights[_data.size()] = weightFirst; this->addData(first, nr, dataStride, nrAccountsForStride); } CASA_STATD void StatisticsAlgorithm::addData( const DataIterator& first, const WeightsIterator& weightFirst, uInt nr, const DataRanges& dataRanges, Bool isInclude, uInt dataStride, Bool nrAccountsForStride ) { _throwIfDataProviderDefined(); _weights[_data.size()] = weightFirst; this->addData( first, nr, dataRanges, isInclude, dataStride, nrAccountsForStride ); } CASA_STATD void StatisticsAlgorithm::addData( const DataIterator& first, const WeightsIterator& weightFirst, const MaskIterator& maskFirst, uInt nr, uInt dataStride, Bool nrAccountsForStride, uInt maskStride ) { _throwIfDataProviderDefined(); _weights[_data.size()] = weightFirst; this->addData( first, maskFirst, nr, dataStride, nrAccountsForStride, maskStride ); } 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 ) { _throwIfDataProviderDefined(); _weights[_data.size()] = weightFirst; this->addData( first, maskFirst, nr, dataRanges, isInclude, dataStride, nrAccountsForStride, maskStride ); } CASA_STATD void StatisticsAlgorithm::deleteSortedArray() { _sortedArray.clear(); } CASA_STATD AccumType StatisticsAlgorithm::getQuantile( Double quantile, CountedPtr knownNpts, CountedPtr knownMin, CountedPtr knownMax, uInt binningThreshholdSizeBytes, Bool persistSortedArray, uInt64 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 ) { reset(); addData(first, nr, dataStride, nrAccountsForStride); } CASA_STATD void StatisticsAlgorithm::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 StatisticsAlgorithm::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 StatisticsAlgorithm::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 StatisticsAlgorithm::setData( const DataIterator& first, const WeightsIterator& weightFirst, uInt nr, uInt dataStride, Bool nrAccountsForStride ) { reset(); addData(first, weightFirst, nr, dataStride, nrAccountsForStride); } CASA_STATD void StatisticsAlgorithm::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 StatisticsAlgorithm::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 StatisticsAlgorithm::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 StatisticsAlgorithm::setStatsToCalculate( std::set& stats ) { _statsToCalculate = stats; } CASA_STATD void StatisticsAlgorithm::setDataProvider( StatsDataProvider *dataProvider ) { ThrowIf(! dataProvider, "Logic Error: data provider cannot be NULL"); reset(); _dataProvider = dataProvider; } CASA_STATD void StatisticsAlgorithm::reset() { _data.clear(); _counts.clear(); _masks.clear(); _weights.clear(); _dataRanges.clear(); _dataStrides.clear(); _maskStrides.clear(); _sortedArray.clear(); _dataProvider = NULL; } CASA_STATD std::map StatisticsAlgorithm::_valuesFromArray( vector& myArray, const std::set& indices ) { //uInt64 largestIdx = *indices.rbegin(); uInt64 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; std::set::const_iterator initer = indices.begin(); std::set::const_iterator inend = indices.end(); Int64 lastIndex = 0; while(initer != inend) { GenSort::kthLargest( &myArray[lastIndex], arySize - lastIndex, *initer - lastIndex ); lastIndex = *initer; ++initer; } std::set::const_iterator iter = indices.begin(); std::set::const_iterator end = indices.end(); while (iter != end) { indexToValuesMap[*iter] = myArray[*iter]; ++iter; } return indexToValuesMap; } CASA_STATD void StatisticsAlgorithm::_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-2.4.1/scimath/Mathematics/StatisticsAlgorithmFactory.h000066400000000000000000000100711321422335000246610ustar00rootroot00000000000000//# 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: aips2-request@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 #include namespace casacore { // Allows a common way for configuring and creating stats algorithm objects template class StatisticsAlgorithmFactory { public: 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; }; // upon construction, the object is configured to use the classical stats algorithm StatisticsAlgorithmFactory(); ~StatisticsAlgorithmFactory(); 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); // Create a pointer to an object of a class derived from StatisticsAlgorithm // that reflects the current configuration CountedPtr > createStatsAlgorithm() const; StatisticsData::ALGORITHM algorithm() const { return _algorithm; } // Throws an exception if the current configuration is not relevant // to the Chauvenet/zscore algorithm 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 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; FitToHalfData _fitToHalfData; ChauvenetData _chauvData; }; } #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif #endif casacore-2.4.1/scimath/Mathematics/StatisticsAlgorithmFactory.tcc000066400000000000000000000203221321422335000252030ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: Array.h 21545 2015-01-22 19:36:35Z gervandiepen $ #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::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 CountedPtr > StatisticsAlgorithmFactory::createStatsAlgorithm() const { casacore::CountedPtr > sa; switch (_algorithm) { case StatisticsData::CLASSICAL: sa = new ClassicalStatistics(); return sa; case StatisticsData::HINGESFENCES: { sa = new HingesFencesStatistics(_hf); return sa; } case StatisticsData::FITTOHALF: { sa = new FitToHalfStatistics( _fitToHalfData.center, _fitToHalfData.side, _fitToHalfData.centerValue ); return sa; } case StatisticsData::CHAUVENETCRITERION: { sa = new ChauvenetCriterionStatistics( _chauvData.zScore, _chauvData.maxIter ); return sa; } default: ThrowCc( "Logic Error: Unhandled algorithm " + String::toString(_algorithm) ); } } 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 typename StatisticsAlgorithmFactory::FitToHalfData StatisticsAlgorithmFactory::fitToHalfData() const { ThrowIf( _algorithm != StatisticsData::FITTOHALF, "Object is currently not configured to use the fit to half algorithm" ); return _fitToHalfData; } CASA_STATD typename StatisticsAlgorithmFactory::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::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("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::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"); FitToHalfStatisticsData::CENTER 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"); FitToHalfStatisticsData::USE_DATA 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"); Double zscore = r.asDouble("zscore"); Int maxIter = r.asInt("max_iter"); saf.configureChauvenet(zscore, maxIter); return saf; } default: ThrowCc( "Logic Error: Unhandled algorithm " + String::toString(algorithm) ); } } } #endif casacore-2.4.1/scimath/Mathematics/StatisticsData.cc000066400000000000000000000060001321422335000224070ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: HostInfoDarwin.h 21521 2014-12-10 08:06:42Z gervandiepen $ #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 QUANTILE: // return "quantile"; 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; std::set::const_iterator fiter = fractions.begin(); std::set::const_iterator fend = fractions.end(); while (fiter != fend) { Double idxWRT1 = *fiter * npts; Double myfloor = floor(idxWRT1); if (near(idxWRT1, myfloor)) { // prevent rounding due to finite machine precision idxWRT1 = myfloor; } fractionToIndexMap[*fiter] = ((uInt64)ceil(idxWRT1) - 1); ++fiter; } return fractionToIndexMap; } } casacore-2.4.1/scimath/Mathematics/StatisticsData.h000066400000000000000000000046261321422335000222650ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: HostInfoDarwin.h 21521 2014-12-10 08:06:42Z gervandiepen $ #ifndef SCIMATH_STATSISTICSDATA_H #define SCIMATH_STATSISTICSDATA_H #include #include #include #include namespace casacore { class String; /* * This class simply defines the enum of supported statistics types * in the statistics framework. */ class StatisticsData { public: // implemented algorithms enum ALGORITHM { 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-2.4.1/scimath/Mathematics/StatisticsIncrementer.h000066400000000000000000000073331321422335000236650ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: Array.h 21545 2015-01-22 19:36:35Z gervandiepen $ #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 StatisticsIncrementer { public: ~StatisticsIncrementer() {} // // NOTE versions with unityStride are deprecated, but left for // backward compatibility. Use versions without unityStride for // new code (and update existing code to use new versions). // loopCount is always incremented by one, independent of the values // of dataStride and maskStride inline static void increment( DataIterator& datum, Int64& loopCount, Bool unityStride, uInt dataStride ); inline static void increment( DataIterator& datum, Int64& loopCount, WeightsIterator& weight, Bool unityStride, uInt dataStride ); inline static void increment( DataIterator& datum, Int64& loopCount, MaskIterator& mask, Bool unityStride, uInt dataStride, uInt maskStride ); inline static void increment( DataIterator& datum, Int64& loopCount, WeightsIterator& weight, MaskIterator& mask, Bool unityStride, uInt dataStride, uInt maskStride ); inline static void increment( DataIterator& datum, Int64& loopCount, uInt dataStride ) { std::advance(datum, dataStride); ++loopCount; } inline static void increment( DataIterator& datum, Int64& loopCount, WeightsIterator& weight, uInt dataStride ) { std::advance(datum, dataStride); std::advance(weight, dataStride); ++loopCount; } inline static void increment( DataIterator& datum, Int64& loopCount, MaskIterator& mask, uInt dataStride, uInt maskStride ) { std::advance(datum, dataStride); std::advance(mask, maskStride); ++loopCount; } inline static void increment( DataIterator& datum, Int64& loopCount, WeightsIterator& weight, MaskIterator& mask, uInt dataStride, uInt maskStride ) { std::advance(datum, dataStride); std::advance(weight, dataStride); std::advance(mask, maskStride); ++loopCount; } // private: // Just static methods, disallow constructor StatisticsIncrementer() {} }; } #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif #endif casacore-2.4.1/scimath/Mathematics/StatisticsIncrementer.tcc000066400000000000000000000053671321422335000242140ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: Array.h 21545 2015-01-22 19:36:35Z gervandiepen $ #ifndef SCIMATH_STATISTICSINCREMENTER_TCC #define SCIMATH_STATISTICSINCREMENTER_TCC #include namespace casacore { template void StatisticsIncrementer::increment( DataIterator& datum, Int64& loopCount, Bool, uInt dataStride ) { std::advance(datum, dataStride); ++loopCount; } template void StatisticsIncrementer::increment( DataIterator& datum, Int64& loopCount, WeightsIterator& weight, Bool, uInt dataStride ) { std::advance(datum, dataStride); std::advance(weight, dataStride); ++loopCount; } template void StatisticsIncrementer::increment( DataIterator& datum, Int64& loopCount, WeightsIterator& weight, MaskIterator& mask, Bool, uInt dataStride, uInt maskStride ) { std::advance(datum, dataStride); std::advance(weight, dataStride); std::advance(mask, maskStride); ++loopCount; } template void StatisticsIncrementer::increment( DataIterator& datum, Int64& loopCount, MaskIterator& mask, Bool, uInt dataStride, uInt maskStride ) { std::advance(datum, dataStride); std::advance(mask, maskStride); ++loopCount; } } #endif casacore-2.4.1/scimath/Mathematics/StatisticsTypes.h000066400000000000000000000045551321422335000225210ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: Array.h 21545 2015-01-22 19:36:35Z gervandiepen $ #ifndef SCIMATH_STATISTICSTYPES_H #define SCIMATH_STATISTICSTYPES_H #include #include #include namespace casacore { class Record; template class CountedPtr; // Commonly used types in statistics framework. #define DataRanges std::vector > typedef std::pair LocationType; template struct StatsData { Bool masked; CountedPtr max; LocationType maxpos; AccumType mean; CountedPtr median; CountedPtr medAbsDevMed; CountedPtr 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-2.4.1/scimath/Mathematics/StatisticsTypes.tcc000066400000000000000000000070751321422335000230430ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: Array.h 21545 2015-01-22 19:36:35Z gervandiepen $ #ifndef SCIMATH_STATISTICSTYPES_TCC #define SCIMATH_STATISTICSTYPES_TCC #include #include #include #include namespace casacore { template StatsData initializeStatsData() { StatsData init = { False, NULL, std::pair(-1, -1), 0, NULL, NULL, NULL, 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.null()) { mycopy.max = new AccumType(*mycopy.max); } if (! mycopy.median.null()) { mycopy.median = new AccumType(*mycopy.median); } if (! mycopy.medAbsDevMed.null()) { mycopy.medAbsDevMed = new AccumType(*mycopy.medAbsDevMed); } if (! mycopy.min.null()) { mycopy.min = 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.null()) { r.define( StatisticsData::toString(StatisticsData::MAX), *stats.max ); r.define("maxDatasetIndex", stats.maxpos.first); r.define("maxIndex", stats.maxpos.second); } if (! stats.min.null()) { r.define( StatisticsData::toString(StatisticsData::MIN), *stats.min ); r.define("minDatasetIndex", stats.minpos.first); r.define("minIndex", stats.minpos.second); } return r; } } #endif casacore-2.4.1/scimath/Mathematics/StatisticsUtilities.h000066400000000000000000000216111321422335000233600ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: Array.h 21545 2015-01-22 19:36:35Z gervandiepen $ #ifndef SCIMATH_STATISTICSUTILITIES_H #define SCIMATH_STATISTICSUTILITIES_H #include #include #include #include #include #include namespace casacore { // Various statistics related methods for the statistics framework. template class StatisticsUtilities { public: // description of a regularly spaced bins with the first bin having lower limit // of minLimit and having nBins equally spaced bins of width binWidth, so that // the upper limit of the last bin is given by minLimit + nBins*binWidth struct BinDesc { AccumType binWidth; AccumType minLimit; uInt nBins; }; ~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 // It's 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& sum, const AccumType& datum, const AccumType& center ); */ /* inline static void waccumulateSym ( Double& npts, AccumType& sumweights, AccumType& wsum, const AccumType& datum, const AccumType& weight, const AccumType& center ); */ 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 ); // // This does the obvious conversions. The Complex and DComplex versions // (implemented after the class definition) are used solely to permit compilation. In general, these versions should // never actually be called inline static Int getInt(const AccumType& v) { return (Int)v; } inline static Bool includeDatum( const AccumType& datum, typename DataRanges::const_iterator beginRange, typename DataRanges::const_iterator endRange, Bool isInclude ); // 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 vector >& stats ); private: const static AccumType TWO; StatisticsUtilities() {} }; // The Complex and DComplex versions // are used solely to permit compilation. In general, these versions should // never actually be called template<> inline Int StatisticsUtilities::getInt(const casacore::Complex&) { ThrowCc("This version for complex data types should never be called"); } template<> inline Int StatisticsUtilities::getInt(const casacore::DComplex&) { ThrowCc("Logic Error: This version for complex data types should never be called"); } template ostream &operator<<(ostream &os, const typename StatisticsUtilities::BinDesc &desc) { os << "min limit " << desc.minLimit << " bin width " << desc.binWidth << " nbins " << desc.nBins; return os; } } #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-2.4.1/scimath/Mathematics/StatisticsUtilities.tcc000066400000000000000000000226361321422335000237120ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: Array.h 21545 2015-01-22 19:36:35Z gervandiepen $ #ifndef SCIMATH_STATISTICSUTILITIES_TCC #define SCIMATH_STATISTICSUTILITIES_TCC #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; \ AccumType prevMean = mean; \ _NLINEAR \ nvariance += (datum - prevMean)*(datum - mean); #define _WQUAD \ wsumsq += weight*datum*datum; \ AccumType 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 _NLINEARSYM \ npts += 2; \ sum += 2*center; \ #define _WLINEARSYM \ npts += 2; \ sumweights += 2*weight; \ wsum += 2*weight*center; \ */ #define _NQUADSYM \ npts += 2; \ AccumType reflect = TWO*center - datum; \ sumsq += datum*datum + reflect*reflect; \ AccumType diff = datum - center; \ nvariance += TWO*diff*diff; #define _WQUADSYM \ npts += 2; \ sumweights += TWO*weight; \ AccumType reflect = TWO*center - datum; \ wsumsq += weight*(datum*datum + reflect*reflect); \ AccumType 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 ) { typename DataRanges::const_iterator riter = beginRange; while (riter != endRange) { if (datum >= (*riter).first && datum <= (*riter).second) { return isInclude; } ++riter; } return ! isInclude; } template StatsData StatisticsUtilities::combine( const vector >& stats ) { StatsData res = initializeStatsData(); typename vector >::const_iterator iter = stats.begin(); typename vector >::const_iterator end = stats.end(); static const AccumType zero = 0; static const AccumType one = 1; for (; iter!=end; ++iter) { if (! iter->max.null()) { if (res.max.null() || *(iter->max) > *res.max) { res.max = iter->max; res.maxpos = iter->maxpos; } } if (! iter->min.null()) { if (res.min.null() || *(iter->min) < *res.min) { res.min = iter->min; res.minpos = iter->minpos; } } AccumType sumweights = iter->sumweights + res.sumweights; AccumType mean = sumweights == zero ? zero : (iter->sumweights*iter->mean + res.sumweights*res.mean)/sumweights; AccumType nvariance = zero; if (sumweights > zero) { AccumType diff1 = iter->mean - mean; AccumType diff2 = res.mean - mean; nvariance = iter->nvariance + res.nvariance + iter->sumweights*diff1*diff1 + res.sumweights*diff2*diff2; } res.masked = iter->masked || res.masked; res.mean = mean; res.npts = iter->npts + res.npts; res.nvariance = nvariance; res.sum = iter->sum + res.sum; res.sumsq = iter->sumsq + res.sumsq; res.sumweights = sumweights; res.variance = res.sumweights > one ? nvariance/(res.sumweights - one) : 0; res.rms = sqrt(res.sumsq/res.sumweights); res.stddev = sqrt(res.variance); res.weighted = iter->weighted || res.weighted; } return res; } } #endif casacore-2.4.1/scimath/Mathematics/StatsDataProvider.h000066400000000000000000000115211321422335000227340ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: Array.h 21545 2015-01-22 19:36:35Z gervandiepen $ #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 // when nontrivial means of doing so are not sufficient. template 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 the current dataset virtual DataIterator getData() = 0; // Get the associated mask of 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 not 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 the associated weights 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 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 std::pair&) {} virtual void updateMinPos(const std::pair&) {} // protected: StatsDataProvider(); }; } #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-2.4.1/scimath/Mathematics/StatsDataProvider.tcc000066400000000000000000000037111321422335000232600ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: Array.h 21545 2015-01-22 19:36:35Z gervandiepen $ #ifndef SCIMATH_STATSDATAPROVIDER_TCC #define SCIMATH_STATSDATAPROVIDER_TCC #include namespace casacore { template StatsDataProvider::StatsDataProvider() {} template StatsDataProvider::~StatsDataProvider() {} template uInt StatsDataProvider::getNMaxThreads() const { return 0; } } #endif casacore-2.4.1/scimath/Mathematics/VanVleck.cc000066400000000000000000000350371321422335000212100ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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; 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) { ScopedMutexLock lock(theirMutex); if (itsSize != npts) { itsSize = npts; initInterpolator(); } } uInt VanVleck::getsize() { return itsSize; } void VanVleck::setQuantization(const Matrix &qx, const Matrix &qy) { ScopedMutexLock 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 rs(itsSize); Vector rhos(itsSize); Double twoN = 2.0*itsSize; Double denom = cos(C::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)*C::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)*C::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)*C::pi/twoN)/denom; // lower side Int lo = midi-i; rhos[lo] = -cos(Double(2*lo+1)*C::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) { ScopedMutexLock lock(theirMutex); rs.resize(itsInterp->getX().nelements()); rs = itsInterp->getX(); rhos.resize(itsInterp->getY().nelements()); rhos = itsInterp->getY(); } Double VanVleck::r(const Double rho) { ScopedMutexLock 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 = C::_2pi*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/C::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 = C::dbl_max; if (x<0) { result = -result; } } return result; } Double VanVleck::invErfc(Double x) { Double result; if (x>=2.0) { result = -C::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 = C::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-2.4.1/scimath/Mathematics/VanVleck.h000066400000000000000000000215501321422335000210450ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 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-2.4.1/scimath/Mathematics/VectorKernel.cc000066400000000000000000000137511321422335000221010ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ // #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) * C::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 * C::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 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 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-2.4.1/scimath/Mathematics/ZScoreCalculator.cc000066400000000000000000000101551321422335000227100ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: HostInfoDarwin.h 21521 2014-12-10 08:06:42Z gervandiepen $ #include #include #include #include namespace casacore { std::map ZScoreCalculator::_nptsToMaxZScore; Mutex ZScoreCalculator::_mutex; Double ZScoreCalculator::getMaxZScore(uInt64 npts) { ScopedMutexLock 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; } std::map::const_iterator iter = _nptsToMaxZScore.find(npts); if (iter != _nptsToMaxZScore.end()) { return iter->second; } std::map::const_iterator lowiter = _nptsToMaxZScore.begin(); std::map::const_iterator upiter = _nptsToMaxZScore.begin(); ++upiter; if (npts > _nptsToMaxZScore.rbegin()->first) { Double zscoreMax = _nptsToMaxZScore.rbegin()->second; Double z = zscoreMax + 0.5; while (True) { uInt64 nptsmin = zscoreToNpts(z); if (nptsmin >= npts) { _nptsToMaxZScore[nptsmin] = z; if (nptsmin == npts) { return z; } else { uInt increment = _nptsToMaxZScore.size() - 2; advance(lowiter, increment); advance(upiter, increment); break; } } z += 0.5; } } else { 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); } } Double lz = lowiter->second; Double uz = upiter->second; Double z = (lz + uz)/2; while (True) { uInt64 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-2.4.1/scimath/Mathematics/ZScoreCalculator.h000066400000000000000000000042311321422335000225500ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: HostInfoDarwin.h 21521 2014-12-10 08:06:42Z gervandiepen $ #ifndef SCIMATH_ZSCORECALCULATOR_H #define SCIMATH_ZSCORECALCULATOR_H #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: // 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 Mutex _mutex; }; } #endif casacore-2.4.1/scimath/Mathematics/test/000077500000000000000000000000001321422335000201375ustar00rootroot00000000000000casacore-2.4.1/scimath/Mathematics/test/CMakeLists.txt000066400000000000000000000012401321422335000226740ustar00rootroot00000000000000set (tests dAutoDiff dSparseDiff tAutoDiff tChauvenetCriterionStatistics tClassicalStatistics tCombinatorics tConvolver tFFTServer tFFTServer2 tFitToHalfStatistics tGaussianBeam tGeometry tHingesFencesStatistics tHistAcc tInterpolateArray1D tMathFunc tMatrixMathLA tMedianSlider tSmooth tSparseDiff tStatAcc tStatisticsAlgorithmFactory tStatisticsTypes tStatisticsUtilities tVanVleck tVectorKernel tZScoreCalculator tInterpolate2D ) foreach (test ${tests}) add_executable (${test} ${test}.cc) target_link_libraries (${test} casa_scimath) add_test (${test} ${CMAKE_SOURCE_DIR}/cmake/cmake_assay ./${test}) add_dependencies(check ${test}) endforeach (test) casacore-2.4.1/scimath/Mathematics/test/dAutoDiff.cc000066400000000000000000000101141321422335000223100ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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-2.4.1/scimath/Mathematics/test/dSparseDiff.cc000066400000000000000000000204451321422335000226450ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: dSparseDiff.cc,v 1.1 2007/11/16 04:44:13 wbrouw Exp $ //# 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); 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; } // 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-2.4.1/scimath/Mathematics/test/tAutoDiff.cc000066400000000000000000000331711321422335000223400ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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-2.4.1/scimath/Mathematics/test/tChauvenetCriterionStatistics.cc000066400000000000000000000166021321422335000265130ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: tStatAcc.cc 20329 2008-06-06 07:59:22Z gervandiepen $ #include #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 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 AipsError& x) { cout << x.getMesg() << endl; return 1; } return 0; } casacore-2.4.1/scimath/Mathematics/test/tClassicalStatistics.cc000066400000000000000000002321111321422335000246030ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: tStatAcc.cc 20329 2008-06-06 07:59:22Z gervandiepen $ #include #include #include #include #include #include #define COMMA , int main() { try { 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}; { ClassicalStatistics::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, 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); cout << "rms " << sd.rms << endl; 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::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; ClassicalStatistics::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, 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 (AipsError& 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::const_iterator, 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::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 AipsError& 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 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 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); } { // 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 AipsError& x) { thrown = True; } AlwaysAssert(thrown, AipsError); thrown = False; try { cs.getQuantile(1); } catch (const AipsError& 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 ? (Float)count : -Float(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); CountedPtr npts; CountedPtr 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); CountedPtr npts; CountedPtr 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; } random_shuffle(big.begin(), big.end()); 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::const_iterator, vector::const_iterator> cs; vector big(1e7); uInt count = 0; vector::iterator iter = big.begin(); vector::iterator end = big.end(); for (; iter!=end; ++iter, ++count) { *iter = count; } cs.addData(big.begin(), big.size()); Double mymin, mymax; cout << "start big" << endl; cs.getMinMax(mymin, mymax); AlwaysAssert(mymin == 0, AipsError); AlwaysAssert(mymax == big.size()-1, AipsError); // do it again, but shuffle the elements random_shuffle(big.begin(), big.end()); ClassicalStatistics::const_iterator, vector::const_iterator> cs1; cs1.addData(big.begin(), big.size()); cs1.getMinMax(mymin, mymax); AlwaysAssert(mymin == 0, AipsError); AlwaysAssert(mymax == big.size()-1, AipsError); } } catch (const AipsError& x) { cout << x.getMesg() << endl; return 1; } return 0; } casacore-2.4.1/scimath/Mathematics/test/tCombinatorics.cc000066400000000000000000000044501321422335000234310ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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 (AipsError) { res = True; } AlwaysAssert(res, AipsError); } cout << "ok" << endl; } catch (AipsError x) { cerr << x.getMesg() << endl; return 1; } return 0; } casacore-2.4.1/scimath/Mathematics/test/tConvolver.cc000066400000000000000000002755431321422335000226270ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/scimath/Mathematics/test/tFFTServer.cc000066400000000000000000002062451321422335000224510ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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 Array input() { Vector input(8, 0.0f); input(0) = 1.0f; return input; } static Array expectedResult() { return Vector(5, Complex(1,0)); } }; template class R2C1Deven2 { public: static Array 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 Array expectedResult() { Vector expectedResult(5, Complex(0,0)); expectedResult(2) = Complex(4,0); return expectedResult; } }; template class R2C1Deven3 { public: static Array 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 Array expectedResult() { Vector expectedResult(5, Complex(0,0)); expectedResult(2) = Complex(0, -4); return expectedResult; } }; template class R2C1Deven4 { public: static Array 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 Array expectedResult() { Vector expectedResult(5, Complex(0,0)); expectedResult(0) = Complex(4, 0); expectedResult(4) = Complex(-4, 0); return expectedResult; } }; template class R2C1Dodd1 { public: static Array input() { Vector input(9, 0.0f); input(0) = 1.0f; return input; } static Array expectedResult() { return Vector(5, Complex(1,0)); } }; template class R2C1Dodd2 { public: static Array input() { return Vector(9, 1.0f); } static Array expectedResult() { Vector expectedResult(5, Complex(0,0)); expectedResult(0) = Complex(9,0); return expectedResult; } }; template class R2C1Dodd3 { public: static Array 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 Array 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 Array input() { Matrix input(4, 6); input = 0.0f; input(0, 0) = 1.0f; return input; } static Array expectedResult() { return Matrix(3, 6, Complex(1, 0)); } }; template class R2C2Deveneven2 { public: static Array input() { return Matrix(4, 6, 1.0f); } static Array expectedResult() { Matrix expectedResult(3, 6, Complex(0,0)); expectedResult(0, 0) = Complex(24, 0); return expectedResult; } }; template class R2C2Deveneven3 { public: static Array 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 Array 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 Array input() { Matrix input(4, 5, 0.0f); input(0, 0) = 1.0f; return input; } static Array expectedResult() { return Matrix(3, 5, Complex(1, 0)); } }; template class R2C2Devenodd2 { public: static Array input() { return Matrix(4, 5, 1.0f); } static Array expectedResult() { Matrix expectedResult(3, 5, Complex(0,0)); expectedResult(0,0) = Complex(20,0); return expectedResult; } }; template class R2C2Doddeven1 { public: static Array input() { Matrix input(3, 6, 0.0f); input(0, 0) = 1.0f; return input; } static Array expectedResult() { return Matrix(2, 6, Complex(1, 0)); } }; template class R2C2Doddeven2 { public: static Array input() { return Matrix(3, 6, 1.0f); } static Array expectedResult() { Matrix expectedResult(2, 6, Complex(0,0)); expectedResult(0,0) = Complex(18,0); return expectedResult; } }; template class R2C2Doddodd1 { public: static Array input() { Matrix input(3, 5, 0.0f); input(0, 0) = 1.0f; return input; } static Array expectedResult() { return Matrix(2, 5, Complex(1, 0)); } }; template class R2C2Doddodd2 { public: static Array input() { return Matrix(3, 5, 1.0f); } static Array expectedResult() { Matrix expectedResult(2, 5, Complex(0,0)); expectedResult(0,0) = Complex(15,0); return expectedResult; } }; template class R2C3Deveneveneven1 { public: static Array input() { Cube input(4, 6, 8, 0.0f); input(0, 0, 0) = 1.0f; return input; } static Array expectedResult() { return Cube(3, 6, 8, Complex(1, 0)); } }; template class R2C3Deveneveneven2 { public: static Array input() { return Cube(4, 6, 8, 1.0f); } static Array 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 Array input() { Cube input(3,5,7, 0.0f); input(0, 0, 0) = 1.0f; return input; } static Array expectedResult() { return Cube(2,5,7, Complex(1, 0)); } }; template class R2C3Doddoddodd2 { public: static Array input() { return Cube(3,5,7, 1.0f); } static Array 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), 0.0f); input(IPosition(4,0)) = 1.0f; return input; } static Array expectedResult() { return Array(IPosition(4,2,5,7,4), Complex(1, 0)); } }; template class R2C4Doddoddoddeven2 { public: static Array input() { return Array (IPosition(4,3,5,7,4), 1.0f); } static Array expectedResult() { Array expectedResult(IPosition(4,2,5,7,4), Complex(0, 0)); expectedResult(IPosition(4,0)) = Complex(3*5*7*4,0); return expectedResult; } static double tolerance() { return 500*FLT_EPSILON; } }; template class C2R1Deven1 { public: static Array input() { Vector input(5, Complex(0, 0)); input(0) = Complex(8, 0); return input; } static Array expectedResult() { return Vector(8, 1.0f); } }; template class C2R1Deven2 { public: static Array input() { Vector input(5, Complex(0, 0)); input(0) = Complex(16.0f, 0.0f); input(2) = Complex(8.0f, 0.0f); return input; } static Array expectedResult() { Vector expectedResult(8, 2.0f); expectedResult(0) = 4.0f; expectedResult(2) = 0.0f; expectedResult(4) = 4.0f; expectedResult(6) = 0.0f; return expectedResult; } }; template class C2R1Deven3 { public: static Array input() { Vector input(5, Complex(0, 0)); input(0) = Complex(0.0f, 0.0f); input(2) = Complex(0.0f, 4.0f); return input; } static Array expectedResult() { Vector expectedResult(8, 0.0f); expectedResult(1) = -1.0f; expectedResult(3) = 1.0f; expectedResult(5) = -1.0f; expectedResult(7) = 1.0f; return expectedResult; } }; template class C2R1Deven4 { public: static Array input() { Vector input(5, Complex(1, 0)); return input; } static Array expectedResult() { Vector expectedResult(8, 0.0f); expectedResult(0) = 1.0f; return expectedResult; } }; template class C2R1Deven5 { public: static Array input() { Vector input(5, Complex(1, 0)); input(1) = Complex(0,0); input(3) = Complex(0,0); return input; } static Array expectedResult() { Vector expectedResult(8, 0.0f); expectedResult(0) = 0.5f; expectedResult(4) = 0.5f; return expectedResult; } }; template class C2R1Dodd1 { public: static Array input() { Vector input(5, Complex(0, 0)); input(0) = Complex(9, 0); return input; } static Array expectedResult() { return Vector(9, 1.0f); } }; template class C2R1Dodd2 { public: static Array input() { Vector input(5, Complex(1, 0)); return input; } static Array expectedResult() { Vector expectedResult(9, 0.0f); expectedResult(0) = 1.0f; return expectedResult; } }; template class C2R2Deveneven1 { public: static Array input() { Matrix input(3, 6, Complex(0, 0)); input(0,0) = Complex(4*6,0); return input; } static Array expectedResult() { Matrix expectedResult(4, 6, 1.0f); return expectedResult; } }; template class C2R2Deveneven2 { public: static Array input() { Matrix input(3, 6, Complex(1, 0)); return input; } static Array expectedResult() { Matrix expectedResult(4, 6, 0.0f); expectedResult(0,0) = 1.0f; return expectedResult; } }; template class C2R2Deveneven3 { public: static Array input() { Matrix input(3, 6, 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); return input; } static Array expectedResult() { Matrix expectedResult(4, 6, 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; return expectedResult; } }; template class C2R2Doddodd1 { public: static Array input() { Matrix input(2, 5, Complex(0, 0)); input(0, 0) = Complex(3*5, 0); return input; } static Array expectedResult() { Matrix expectedResult(3, 5, 1.0f); return expectedResult; } }; template class C2R2Doddodd2 { public: static Array input() { Matrix input(2, 5, Complex(1, 0)); return input; } static Array expectedResult() { Matrix expectedResult(3, 5, 0.0f); expectedResult(0,0) = 1.0f; return expectedResult; } }; template class C2R2Doddodd3 { public: static Array input() { Matrix input(2, 5, 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); return input; } static Array expectedResult() { Matrix expectedResult(3, 5, 0.0f); expectedResult(1,0) = -25.9808f; expectedResult(2,0) = 25.9808f; return expectedResult; } }; template class C2R2Devenodd1 { public: static Array input() { Matrix input(3, 5, Complex(0, 0)); input(0,0) = Complex(4*5, 0); return input; } static Array expectedResult() { Matrix expectedResult(4, 5, 1.0f); return expectedResult; } }; template class C2R2Devenodd2 { public: static Array input() { Matrix input(3, 5, Complex(1, 0)); return input; } static Array expectedResult() { Matrix expectedResult(4, 5, 0.0f); expectedResult(0,0) = 1.0f; return expectedResult; } }; template class C2R2Doddeven1 { public: static Array input() { Matrix input(2, 6, Complex(0, 0)); input(0,0) = Complex(3*6, 0); return input; } static Array expectedResult() { Matrix expectedResult(3, 6, 1.0f); return expectedResult; } }; template class C2R2Doddeven2 { public: static Array input() { Matrix input(2, 6, Complex(1, 0)); return input; } static Array expectedResult() { Matrix expectedResult(3, 6, 0.0f); expectedResult(0,0) = 1.0f; return expectedResult; } }; template class C2R3Deveneveneven1 { public: static Array input() { Cube input(3, 6, 2, Complex(0, 0)); input(0,0,0) = Complex(4*6*2,0); return input; } static Array expectedResult() { Cube expectedResult(4, 6, 2, 1.0f); return expectedResult; } }; template class C2R3Deveneveneven2 { public: static Array input() { Cube input(3, 6, 2, Complex(1, 0)); return input; } static Array expectedResult() { Cube expectedResult(4, 6, 2, 0.0f); expectedResult(0,0,0) = 1.0f; return expectedResult; } }; template class C2R3Doddoddodd1 { public: static Array input() { Cube input(2, 5, 7, Complex(0, 0)); input(0,0,0) = Complex(3*5*7,0); return input; } static Array expectedResult() { Cube expectedResult(3, 5, 7, 1.0f); return expectedResult; } }; template class C2R3Doddoddodd2 { public: static Array input() { Cube input(2, 5, 7, Complex(1, 0)); return input; } static Array expectedResult() { Cube expectedResult(3, 5, 7, 0.0f); 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), Complex(0, 0)); #else Array input(IPosition(4,2,5,7,2), Complex(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), 1.0f); #endif expectedResult = 1.0f; return expectedResult; } }; template class C2R4Doddoddoddeven2 { public: static Array input() { Array input(IPosition(4,2,5,7,2), Complex(1, 0)); return input; } static Array expectedResult() { Array expectedResult(IPosition(4,3,5,7,2), 0.0f); expectedResult = 0.0f; expectedResult(IPosition(4,0)) = 1.0f; return expectedResult; } }; template class C2C1Deven1 { public: static Array input() { Vector input(8, Complex(0, 0)); input(0) = Complex(1.0f, 0.0f); return input; } static Array expectedResult() { Vector expectedResult(8, Complex(1, 0)); return expectedResult; } }; template class C2C1Deven2 { public: static Array input() { Vector input(8, Complex(1, 0)); return input; } static Array expectedResult() { Vector expectedResult(8, Complex(0, 0)); expectedResult(0) = Complex(8,0); return expectedResult; } }; template class C2C1Deven3 { public: static Array input() { Vector input(8, Complex(-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 Array expectedResult() { Vector expectedResult(8, Complex(0, 0)); expectedResult(4) = Complex(8,0); return expectedResult; } }; template class C2C1Deven4 { public: static Array input() { Vector input(8, Complex(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 Array expectedResult() { Vector expectedResult(8, Complex(0, 0)); expectedResult(2) = Complex(0,-4); expectedResult(6) = Complex(0,4); return expectedResult; } }; template class C2C1Dodd1 { public: static Array input() { Vector input(7, Complex(0, 0)); input(0) = Complex(1.0f, 0.0f); return input; } static Array expectedResult() { Vector expectedResult(7, Complex(1, 0)); return expectedResult; } }; template class C2C1Dodd2 { public: static Array input() { Vector input(7, Complex(1, 0)); return input; } static Array expectedResult() { Vector expectedResult(7, Complex(0, 0)); expectedResult(0) = Complex(7,0); return expectedResult; } }; template class C2C2Deveneven1 { public: static Array input() { Matrix input(4, 6, Complex(0, 0)); input(0,0) = Complex(1,0); return input; } static Array expectedResult() { Matrix expectedResult(4, 6, Complex(1, 0)); return expectedResult; } }; template class C2C2Deveneven2 { public: static Array input() { Matrix input(4, 6, Complex(1, 0)); return input; } static Array expectedResult() { Matrix expectedResult(4, 6, Complex(0, 0)); expectedResult(0,0) = Complex(24,0); return expectedResult; } }; template class C2C2Deveneven3 { public: static Array input() { Matrix input(4, 6, Complex(0, 0)); input(1,1) = Complex(1,1); input(1,3) = Complex(1,1); input(1,5) = Complex(1,1); return input; } static Array expectedResult() { Matrix expectedResult(4, 6, 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); return expectedResult; } }; template class C2C2Doddodd1 { public: static Array input() { Matrix input(3, 5, Complex(0, 0)); input(0,0) = Complex(1,0); return input; } static Array expectedResult() { Matrix expectedResult(3, 5, Complex(1, 0)); return expectedResult; } }; template class C2C2Doddodd2 { public: static Array input() { Matrix input(3, 5, Complex(1, 0)); return input; } static Array expectedResult() { Matrix expectedResult(3, 5, Complex(0, 0)); expectedResult(0,0) = Complex(15,0); return expectedResult; } }; template class C2C2Devenodd1 { public: static Array input() { Matrix input(4, 5, Complex(0, 0)); input(0,0) = Complex(1,0); return input; } static Array expectedResult() { Matrix expectedResult(4, 5, Complex(1, 0)); return expectedResult; } }; template class C2C2Devenodd2 { public: static Array input() { Matrix input(4, 5, Complex(1, 0)); return input; } static Array expectedResult() { Matrix expectedResult(4, 5, Complex(0, 0)); expectedResult(0,0) = Complex(20,0); return expectedResult; } }; template class C2C2Doddeven1 { public: static Array input() { Matrix input(3, 6, Complex(0, 0)); input(0,0) = Complex(1,0); return input; } static Array expectedResult() { Matrix expectedResult(3, 6, Complex(1, 0)); return expectedResult; } }; template class C2C2Doddeven2 { public: static Array input() { Matrix input(3, 6, Complex(1, 0)); return input; } static Array expectedResult() { Matrix expectedResult(3, 6, Complex(0, 0)); expectedResult(0,0) = Complex(18,0); return expectedResult; } }; template class C2C3Doddeveneven1 { public: static Array input() { Cube input(4, 6, 8, Complex(0, 0)); input(0,0,0) = Complex(1,0); return input; } static Array expectedResult() { Cube expectedResult(4, 6, 8, Complex(1, 0)); return expectedResult; } }; template class C2C3Doddeveneven2 { public: static Array input() { Cube input(4, 6, 8, Complex(1, 0)); return input; } static Array expectedResult() { Cube expectedResult(4, 6, 8, Complex(0, 0)); expectedResult(0,0,0) = Complex(4*6*8,0); return expectedResult; } }; template class C2C3Doddoddodd1 { public: static Array input() { Cube input(3, 5, 7, Complex(0, 0)); input(0,0,0) = Complex(1,0); return input; } static Array expectedResult() { Cube expectedResult(3, 5, 7, Complex(1, 0)); return expectedResult; } }; template class C2C3Doddoddodd2 { public: static Array input() { Cube input(3, 5, 7, Complex(1, 0)); return input; } static Array expectedResult() { Cube expectedResult(3, 5, 7, Complex(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 (AipsError x) { cerr << x.getMesg() << endl; cout << "FAIL" << endl; return 1; } cout << tests_done << " tests OK" << endl; return 0; } casacore-2.4.1/scimath/Mathematics/test/tFFTServer2.cc000066400000000000000000001355711321422335000225360ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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 (AipsError x) { cerr << x.getMesg() << endl; cout << "FAIL" << endl; return 1; } cout << "OK" << endl; return 0; } // Local Variables: // compile-command: "gmake OPTLIB=1 tFFTServer" // End: casacore-2.4.1/scimath/Mathematics/test/tFitToHalfStatistics.cc000066400000000000000000002437201321422335000245350ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: tStatAcc.cc 20329 2008-06-06 07:59:22Z gervandiepen $ #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; vector v1(3); v1[0] = 5; v1[1] = 8; v1[2] = 10; { FitToHalfStatistics::const_iterator, 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 (AipsError& 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 AipsError& 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 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 AipsError& x) { thrown = True; } AlwaysAssert(thrown, AipsError); thrown = False; try { fh.getQuantile(1); } catch (const AipsError& x) { thrown = True; } AlwaysAssert(thrown, AipsError); } { // getQuantile(), no weights, no mask, no ranges FitToHalfStatistics::const_iterator, 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::const_iterator, 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() vector big(100000, 30); FitToHalfStatistics::const_iterator, 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); CountedPtr npts; CountedPtr 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; CountedPtr npts; CountedPtr 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); } } catch (const AipsError& x) { cout << x.getMesg() << endl; return 1; } return 0; } casacore-2.4.1/scimath/Mathematics/test/tGaussianBeam.cc000066400000000000000000000122461321422335000231760ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: HostInfoDarwin.h 21521 2014-12-10 08:06:42Z gervandiepen $ #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 (AipsError x) { cout << "Exception thrown as expected: " << x.getMesg() << endl; except = True; } AlwaysAssert(except, AipsError); except = False; try { // major smaller than minor majAx = Quantity(2, "arcsec"); GaussianBeam beam3(majAx, minAx, pa); } catch (AipsError x) { cout << "Exception thrown as expected: " << x.getMesg() << endl; except = True; } AlwaysAssert(except, AipsError); // getArea majAx = Quantity(1, "arcsec"); minAx = majAx; beam = GaussianBeam(majAx, minAx, pa); AlwaysAssert(beam.getArea("arcsec2") == Quantity(C::pi/4/C::ln2), AipsError); except = False; try { // bogus units beam.getArea("arcsec"); } catch (AipsError x) { cout << "Exception thrown as expected: " << x.getMesg() << 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 (AipsError x) { cout << "Exception thrown as expected: " << x.getMesg() << 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); } } } catch (AipsError x) { cout << x.getMesg() << endl; cout << "FAIL" << endl; return 1; } cout << "OK" << endl; return 0; } casacore-2.4.1/scimath/Mathematics/test/tGeometry.cc000066400000000000000000000070631321422335000224330ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: HostInfoDarwin.h 21521 2014-12-10 08:06:42Z gervandiepen $ #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 AipsError& x) { cerr << x.getMesg() << endl; return 1; } return 0; } casacore-2.4.1/scimath/Mathematics/test/tHingesFencesStatistics.cc000066400000000000000000003602131321422335000252530ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: tStatAcc.cc 20329 2008-06-06 07:59:22Z gervandiepen $ #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 (AipsError& 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 AipsError& 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 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 AipsError& x) { thrown = True; } AlwaysAssert(thrown, AipsError); thrown = False; try { cs.getQuantile(1); } catch (const AipsError& 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); CountedPtr npts; CountedPtr 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 AipsError& 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 AipsError& x) { thrown = True; } AlwaysAssert(thrown, AipsError); thrown = False; try { hfs.getQuantile(1); } catch (const AipsError& 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); CountedPtr npts; CountedPtr 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 AipsError& x) { cout << x.getMesg() << endl; return 1; } return 0; } casacore-2.4.1/scimath/Mathematics/test/tHistAcc.cc000066400000000000000000000110641321422335000221520ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { cout << x.getMesg() << endl; return 1; // unexpected error } return 0; // exit with success status } casacore-2.4.1/scimath/Mathematics/test/tHistAcc.out000066400000000000000000000073461321422335000224040ustar00rootroot00000000000000 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-2.4.1/scimath/Mathematics/test/tInterpolate2D.cc000066400000000000000000000072301321422335000233100ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# 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 { 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 vector methods(4); methods[0] = "linear"; methods[1] = "cubic"; methods[2] = "lanczos"; methods[3] = "nearest"; vector results(4); results[0] = 9.552; // Linear results[1] = 9.552; // Cubic results[2] = 9.473654921656; // Lanczos results[3] = 9.; // Nearest for (uInt method=0; method #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 (AipsError x) { cerr << x.getMesg() << endl; cout << "FAIL" << endl; return 1; } cout << tests_done << " tests OK" << endl; return 0; } casacore-2.4.1/scimath/Mathematics/test/tMathFunc.cc000066400000000000000000000212311321422335000223360ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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(AipsError x) { cout << "Unexpected exception: " << x.getMesg() << 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-2.4.1/scimath/Mathematics/test/tMatrixMathLA.cc000066400000000000000000000047531321422335000231360ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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-2.4.1/scimath/Mathematics/test/tMedianSlider.cc000066400000000000000000000123731321422335000232000ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //#! 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-2.4.1/scimath/Mathematics/test/tSmooth.cc000066400000000000000000000356311321422335000221130ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include #include #include #include #include #include #include #include #include int main() { Bool anyFailures = False; { Bool failed = False; // Test with Float Vectors, all flags 0 vector vyin; 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; 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; 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; 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; 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-2.4.1/scimath/Mathematics/test/tStatAcc.cc000066400000000000000000000112221321422335000221520ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { cout << x.getMesg() << endl; return 1; // unexpected error } return 0; // exit with success status } casacore-2.4.1/scimath/Mathematics/test/tStatisticsAlgorithmFactory.cc000066400000000000000000000103601321422335000261630ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# #include #include #include #if __cplusplus < 201103L # define nullptr NULL #endif 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 ); StatisticsAlgorithmFactory::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 ); StatisticsAlgorithmFactory::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); } catch (const AipsError& x) { cout << x.getMesg() << endl; return 1; } return 0; } casacore-2.4.1/scimath/Mathematics/test/tStatisticsTypes.cc000066400000000000000000000120061321422335000240100ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# #include #include #include #if __cplusplus < 201103L # define nullptr NULL #endif int main() { // Unit tests for toRecord(const StatsData& stats) from // StatisticsTypes. try { struct StatsData stats; stats.masked = True; stats.max = new Double(27.3); stats.maxpos = std::make_pair(2, 55); stats.mean = 22.1; stats.median = new Double(22.8); stats.medAbsDevMed = new Double(1.3); stats.min = 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 AipsError& x) { cout << x.getMesg() << endl; return 1; } return 0; } casacore-2.4.1/scimath/Mathematics/test/tStatisticsUtilities.cc000066400000000000000000000262351321422335000246700ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: tStatAcc.cc 20329 2008-06-06 07:59:22Z gervandiepen $ #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 AipsError& x) { cout << x.getMesg() << endl; return 1; } return 0; } casacore-2.4.1/scimath/Mathematics/test/tVanVleck.cc000066400000000000000000000434711321422335000223540ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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(C::pi/twoN); for (uInt i=0;i<65;i++) { Double rho = -cos(Double(2*i+1)*C::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 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 AipsError& x) { cout << x.getMesg() << endl; return 1; } return 0; } casacore-2.4.1/scimath/scimath.dox000066400000000000000000000033711321422335000170710ustar00rootroot00000000000000//# 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: aips2-request@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-2.4.1/scimath_f/000077500000000000000000000000001321422335000152265ustar00rootroot00000000000000casacore-2.4.1/scimath_f/ADDGRD.f000066400000000000000000000131471321422335000163300ustar00rootroot00000000000000*----------------------------------------------------------------------- * 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: aips2-request@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-2.4.1/scimath_f/CMakeLists.txt000066400000000000000000000012141321422335000177640ustar00rootroot00000000000000# # CASA scimath_f # add_library ( casa_scimath_f abshis.f absmax.f ADDGRD.f atmroutines.f convolvegridder.f dfftpak.f dqags.f fftpak.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 ) 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-2.4.1/scimath_f/abshis.f000066400000000000000000000315571321422335000166610ustar00rootroot00000000000000*----------------------------------------------------------------------- * 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: aips2-request@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-2.4.1/scimath_f/absmax.f000066400000000000000000000170031321422335000166510ustar00rootroot00000000000000*----------------------------------------------------------------------- * 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: aips2-request@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-2.4.1/scimath_f/atmroutines.f000066400000000000000000000731221321422335000177540ustar00rootroot00000000000000* 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 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 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-2.4.1/scimath_f/convolvegridder.f000066400000000000000000000424421321422335000205770ustar00rootroot00000000000000*======================================================================= * 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: aips2-request@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-2.4.1/scimath_f/dfftpak.f000066400000000000000000002702651321422335000170300ustar00rootroot00000000000000*======================================================================= C Correspondence concerning AIPS++ should be addressed as follows: C Internet email: aips2-request@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-2.4.1/scimath_f/dqags.f000066400000000000000000001440331321422335000165010ustar00rootroot00000000000000*----------------------------------------------------------------------- * 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-2.4.1/scimath_f/fftpak.f000066400000000000000000012103741321422335000166600ustar00rootroot00000000000000*======================================================================= C Correspondence concerning AIPS++ should be addressed as follows: C Internet email: aips2-request@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-2.4.1/scimath_f/fgridft.f000066400000000000000000000203231321422335000170220ustar00rootroot00000000000000*======================================================================= * 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: aips2-request@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-2.4.1/scimath_f/fgridsd.f000066400000000000000000000355201321422335000170240ustar00rootroot00000000000000*======================================================================= * 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: aips2-request@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-2.4.1/scimath_f/fmosft.f000066400000000000000000000332761321422335000167060ustar00rootroot00000000000000*======================================================================= * 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: aips2-request@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-2.4.1/scimath_f/fwproj.f000066400000000000000000000233361321422335000167130ustar00rootroot00000000000000*======================================================================= * 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: aips2-request@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-2.4.1/scimath_f/getbig.f000066400000000000000000000300121321422335000166320ustar00rootroot00000000000000*----------------------------------------------------------------------- * 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: aips2-request@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-2.4.1/scimath_f/grd2d.f000066400000000000000000000146671321422335000164150ustar00rootroot00000000000000*======================================================================= * 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: aips2-request@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-2.4.1/scimath_f/grd2dwts.f000066400000000000000000000150211321422335000171340ustar00rootroot00000000000000*======================================================================= * 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: aips2-request@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-2.4.1/scimath_f/grdde2d.f000066400000000000000000000140201321422335000167050ustar00rootroot00000000000000*======================================================================= * 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: aips2-request@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-2.4.1/scimath_f/grdgauss.f000066400000000000000000000035471321422335000172250ustar00rootroot00000000000000*======================================================================= * 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: aips2-request@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-2.4.1/scimath_f/grdjinc1.f000066400000000000000000000073741321422335000171110ustar00rootroot00000000000000*======================================================================= * 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: aips2-request@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-2.4.1/scimath_f/grdsf.f000066400000000000000000000060361321422335000165070ustar00rootroot00000000000000*======================================================================= * 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: aips2-request@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-2.4.1/scimath_f/hclean.f000066400000000000000000000147231321422335000166360ustar00rootroot00000000000000*======================================================================= * 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: aips2-request@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-2.4.1/scimath_f/lawson.f000066400000000000000000001106051321422335000167030ustar00rootroot00000000000000*======================================================================= * $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 80 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 40 L = J,N H(L) = SZERO DO 30 I = J,M 30 H(L) = H(L) + A(I,L)**2 IF (H(L).GT.H(LMAX)) LMAX = L 40 CONTINUE 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 60 I = 1,M TMP = A(I,J) A(I,J) = A(I,LMAX) 60 A(I,LMAX) = TMP 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) 80 CALL H12 (2,J,J+1,M,A(1,J),1,H(J),B,1,MDB,NB) C C DETERMINE THE PSEUDORANK, K, USING THE TOLERANCE, TAU. C DO 90 J = 1,LDIAG IF (ABS(A(J,J)).LE.TAU) GOTO 100 90 CONTINUE 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 130 JB = 1,NB TMP = SZERO IF (KP1.GT.M) GOTO 130 DO 120 I = KP1,M 120 TMP = TMP + B(I,JB)**2 130 RNORM(JB) = SQRT(TMP) 140 CONTINUE C SPECIAL FOR PSEUDORANK = 0 IF (K.GT.0) GOTO 160 IF (NB.LE.0) GOTO 270 DO 150 JB = 1,NB DO 150 I = 1,N 150 B(I,JB) = SZERO 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 170 II = 1,K I = KP1 - II 170 CALL H12 (1,I,KP1,N,A(I,1),MDA,G(I),A,MDA,1,I-1) 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 210 L = 1,K SM = DZERO I = KP1 - L IF (I.EQ.K) GOTO 200 IP1 = I + 1 DO 190 J = IP1,K 190 SM = SM + A(I,J)*DBLE(B(J,JB)) 200 SM1 = SM 210 B(I,JB) = (B(I,JB)-SM1)/A(I,I) C IF (K.EQ.N) GOTO 240 DO 220 J = KP1,N 220 B(J,JB) = SZERO DO 230 I = 1,K 230 CALL H12 (2,I,KP1,N,A(I,1),MDA,G(I),B(1,JB),1,MDB,1) 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 10 J=1,N 10 X(J)=ZERO 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 30 J=1,M DO 20 I=1,N IW=IW+1 20 W(IW)=G(J,I) IW=IW+1 30 W(IW)=H(J) IF=IW+1 C STORE N ZEROS FOLLOWED BY A ONE INTO F. DO 40 I=1,N IW=IW+1 40 W(IW)=ZERO 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 60 I=1,M IW=IW+1 C HERE WE ARE USING THE SOLUTION VECTOR Y. 60 FAC=FAC-H(I)*W(IW) C C IF (DIFF(ONE+FAC,ONE)) 130,130,70 IF (DIFF(ONE+FAC,ONE) .LE. 0) GOTO 130 70 FAC=ONE/FAC DO 90 J=1,N IW=IY-1 DO 80 I=1,M IW=IW+1 C HERE WE ARE USING THE SOLUTION VECTOR Y. 80 X(J)=X(J)+G(I,J)*W(IW) 90 X(J)=X(J)*FAC DO 100 J=1,N 100 XNORM=XNORM+X(J)**2 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, 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 20 I=1,N X(I)=ZERO 20 INDEX(I)=I 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 50 IZ=IZ1,IZ2 J=INDEX(IZ) SM=ZERO DO 40 L=NPP1,M 40 SM=SM+A(L,J)*B(L) 50 W(J)=SM 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 90 L=1,NSETP 90 UNORM=UNORM+A(L,J)**2 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 120 L=1,M 120 ZZ(L)=B(L) 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 150 L=1,M 150 B(L)=ZZ(L) 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 160 JZ=IZ1,IZ2 JJ=INDEX(JZ) 160 CALL H12 (2,NSETP,NPP1,M,A(1,J),1,UP,A(1,JJ),1,MDA,1) 170 CONTINUE C IF (NSETP.EQ.M) GO TO 190 DO 180 L=NPP1,M 180 A(L,J)=ZERO 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 250 IP=1,NSETP L=INDEX(IP) 250 X(L)=X(L)+ALPHA*(ZZ(IP)-X(L)) 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 280 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 270 L=1,N IF (L.NE.II) CALL G2 (CC,SS,A(J-1,L),A(J,L)) 270 CONTINUE 280 CALL G2 (CC,SS,B(J-1),B(J)) 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 310 I=1,M 310 ZZ(I)=B(I) C ASSIGN 320 TO NEXT NEXT = 2 GO TO 400 320 CONTINUE GO TO 210 C ****** END OF SECONDARY LOOP ****** C 330 DO 340 IP=1,NSETP I=INDEX(IP) 340 X(I)=ZZ(IP) 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 360 I=NPP1,M 360 SM=SM+B(I)**2 GO TO 390 370 DO 380 J=1,N 380 W(J)=ZERO 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 430 L=1,NSETP IP=NSETP+1-L IF (L.EQ.1) GO TO 420 DO 410 II=1,IP 410 ZZ(II)=ZZ(II)-A(II,JJ)*ZZ(IP+1) 420 JJ=INDEX(IP) 430 ZZ(IP)=ZZ(IP)/A(IP,JJ) 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 10 J=L1,M 10 CL=AMAX1(ABS(U(1,J)),CL) C IF (CL) 130,130,20 IF (CL .LE. 0) GOTO 130 20 CLINV=ONE/CL SM=(DBLE(U(1,LPIVOT))*CLINV)**2 DO 30 J=L1,M 30 SM=SM+(DBLE(U(1,J))*CLINV)**2 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 90 I=L1,M SM=SM+C(I3)*DBLE(U(1,I)) 90 I3=I3+ICE C IF (SM) 100,120,100 IF (SM .EQ. 0) GOTO 120 100 SM=SM*B C(I2)=C(I2)+SM*DBLE(UP) DO 110 I=L1,M C(I4)=C(I4)+SM*DBLE(U(1,I)) 110 I4=I4+ICE 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 10 J=1,N 10 X(J)=ZERO 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 30 J=1,M DO 20 I=1,N IW=IW+1 20 W(IW)=G(J,I) IW=IW+1 30 W(IW)=H(J) IF=IW+1 C STORE N ZEROS FOLLOWED BY A ONE INTO F. DO 40 I=1,N IW=IW+1 40 W(IW)=ZERO 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 60 I=1,M IW=IW+1 C HERE WE ARE USING THE SOLUTION VECTOR Y. 60 FAC=FAC-H(I)*W(IW) C C IF (DDIFF(ONE+FAC,ONE)) 130,130,70 IF (DDIFF(ONE+FAC,ONE) .LE. 0) GOTO 130 70 FAC=ONE/FAC DO 90 J=1,N IW=IY-1 DO 80 I=1,M IW=IW+1 C HERE WE ARE USING THE SOLUTION VECTOR Y. 80 X(J)=X(J)+G(I,J)*W(IW) 90 X(J)=X(J)*FAC DO 100 J=1,N 100 XNORM=XNORM+X(J)**2 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) 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 20 I=1,N X(I)=ZERO 20 INDEX(I)=I 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 50 IZ=IZ1,IZ2 J=INDEX(IZ) SM=ZERO DO 40 L=NPP1,M 40 SM=SM+A(L,J)*B(L) 50 W(J)=SM 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 90 L=1,NSETP 90 UNORM=UNORM+A(L,J)**2 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 120 L=1,M 120 ZZ(L)=B(L) 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 150 L=1,M 150 B(L)=ZZ(L) 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 160 JZ=IZ1,IZ2 JJ=INDEX(JZ) 160 CALL DH12 (2,NSETP,NPP1,M,A(1,J),1,UP,A(1,JJ),1,MDA,1) 170 CONTINUE C IF (NSETP.EQ.M) GO TO 190 DO 180 L=NPP1,M 180 A(L,J)=ZERO 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 250 IP=1,NSETP L=INDEX(IP) 250 X(L)=X(L)+ALPHA*(ZZ(IP)-X(L)) 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 280 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 280 CALL DG2 (CC,SS,B(J-1),B(J)) 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 310 I=1,M 310 ZZ(I)=B(I) C ASSIGN 320 TO NEXT NEXT = 2 GO TO 400 320 CONTINUE GO TO 210 C ****** END OF SECONDARY LOOP ****** C 330 DO 340 IP=1,NSETP I=INDEX(IP) 340 X(I)=ZZ(IP) 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 360 I=NPP1,M 360 SM=SM+B(I)**2 GO TO 390 370 DO 380 J=1,N 380 W(J)=ZERO 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 430 L=1,NSETP IP=NSETP+1-L IF (L.EQ.1) GO TO 420 DO 410 II=1,IP 410 ZZ(II)=ZZ(II)-A(II,JJ)*ZZ(IP+1) 420 JJ=INDEX(IP) 430 ZZ(IP)=ZZ(IP)/A(IP,JJ) 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 10 J=L1,M 10 CL=DMAX1(ABS(U(1,J)),CL) C IF (CL) 130,130,20 IF (CL .LE. 0) GOTO 130 20 CLINV=ONE/CL SM=(DBLE(U(1,LPIVOT))*CLINV)**2 DO 30 J=L1,M 30 SM=SM+(DBLE(U(1,J))*CLINV)**2 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 90 I=L1,M SM=SM+C(I3)*DBLE(U(1,I)) 90 I3=I3+ICE C IF (SM) 100,120,100 IF (SM .EQ. 0) GOTO 120 100 SM=SM*B C(I2)=C(I2)+SM*DBLE(UP) DO 110 I=L1,M C(I4)=C(I4)+SM*DBLE(U(1,I)) 110 I4=I4+ICE 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-2.4.1/scimath_f/maxabs.f000066400000000000000000000235201321422335000166520ustar00rootroot00000000000000*----------------------------------------------------------------------- * 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: aips2-request@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-2.4.1/scimath_f/parametricsolver.f000066400000000000000000001373461321422335000207750ustar00rootroot00000000000000*======================================================================= * 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: aips2-request@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-2.4.1/scimath_f/phasol.f000066400000000000000000000161321321422335000166660ustar00rootroot00000000000000*======================================================================= * 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 * * $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-2.4.1/scimath_f/subcom.f000066400000000000000000000174601321422335000166750ustar00rootroot00000000000000*----------------------------------------------------------------------- * 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: aips2-request@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-2.4.1/scimath_f/vvroutines.f000066400000000000000000000504001321422335000176200ustar00rootroot00000000000000*----------------------------------------------------------------------- * 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: aips2-request@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-2.4.1/tables/000077500000000000000000000000001321422335000145435ustar00rootroot00000000000000casacore-2.4.1/tables/CMakeLists.txt000066400000000000000000000244721321422335000173140ustar00rootroot00000000000000# # CASA Tables # set ( parser_inputs RecordGram TableGram ) foreach (src ${parser_inputs}) BISON_TARGET (${src} TaQL/${src}.yy ${CMAKE_CURRENT_BINARY_DIR}/${src}.ycc COMPILE_FLAGS "-y -p ${src}") 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) add_library (casa_tables Tables/ArrayColumn_tmpl.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/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 DataMan/BitFlagsEngine.cc DataMan/CompressComplex.cc DataMan/CompressFloat.cc DataMan/DataManAccessor.cc DataMan/DataManError.cc DataMan/DataManInfo.cc DataMan/DataManager.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/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/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/ExprRange.cc TaQL/ExprUDFNode.cc TaQL/ExprUDFNodeArray.cc TaQL/ExprUnitNode.cc TaQL/MArrayBase.cc TaQL/RecordExpr.cc TaQL/RecordGram.cc TaQL/TaQLNode.cc TaQL/TaQLNodeDer.cc TaQL/TaQLNodeHandler.cc TaQL/TaQLNodeRep.cc TaQL/TaQLNodeResult.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/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} ) target_link_libraries (casa_tables casa_casa ${CASACORE_ARCH_LIBS}) add_subdirectory (apps) install (TARGETS casa_tables 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 Tables/ArrColData.h Tables/ArrColData.tcc Tables/ArrColDesc.h Tables/ArrColDesc.tcc Tables/ArrayColumn.h Tables/ArrayColumn.tcc 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/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/TableVector.h Tables/TableVector.tcc DESTINATION include/casacore/tables/Tables ) install (FILES 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/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/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/VSCEngine.h DataMan/VSCEngine.tcc DataMan/VirtArrCol.h DataMan/VirtArrCol.tcc DataMan/VirtColEng.h DataMan/VirtScaCol.h DataMan/VirtScaCol.tcc DataMan/VirtualTaQLColumn.h 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/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/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/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 DataMan.h LogTables.h Tables.h TaQL.h DESTINATION include/casacore/tables ) # Temporary install of stub header files. install (FILES Tables/BitFlagsEngine.h Tables/CompressComplex.h Tables/CompressFloat.h Tables/DataManAccessor.h Tables/DataManError.h Tables/DataManager.h Tables/ExprNode.h Tables/ExprNodeSet.h Tables/ForwardCol.h Tables/ForwardColRow.h Tables/IncrStManAccessor.h Tables/IncrementalStMan.h Tables/RecordGram.h Tables/ScaledArrayEngine.h Tables/ScaledComplexData.h Tables/StManAipsIO.h Tables/StandardStMan.h Tables/StandardStManAccessor.h Tables/TSMOption.h Tables/TableExprId.h Tables/TableParse.h Tables/TiledCellStMan.h Tables/TiledColumnStMan.h Tables/TiledDataStMan.h Tables/TiledDataStManAccessor.h Tables/TiledShapeStMan.h Tables/TiledStManAccessor.h Tables/UDFBase.h DESTINATION include/casacore/tables/Tables ) add_subdirectory (Tables/test ${EXCL_ALL}) add_subdirectory (DataMan/test ${EXCL_ALL}) add_subdirectory (TaQL/test ${EXCL_ALL}) add_subdirectory (LogTables/test ${EXCL_ALL}) casacore-2.4.1/tables/DataMan.h000066400000000000000000000067251321422335000162330ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: Tables.h 21434 2014-05-07 13:07:20Z gervandiepen $ #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-2.4.1/tables/DataMan/000077500000000000000000000000001321422335000160505ustar00rootroot00000000000000casacore-2.4.1/tables/DataMan/BaseMappedArrayEngine.h000066400000000000000000000467631321422335000223670ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (uInt rownr, Array& data); // //
          // void putArray (uInt 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 (uInt rownr, const Slicer& slicer, Array& data); // //
          // void putSlice (uInt 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 (uInt rownr, const IPosition& shape); // //
          // uInt ndim (uInt rownr); // //
          // IPosition shape (uInt 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 create (uInt nrrow); // //
          // void open (uInt 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 addRow (uInt nrrow); // //
          // void removeRow (uInt 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 (uInt 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 only used by copy constructor of derived classes. // (so it is made protected). BaseMappedArrayEngine (const BaseMappedArrayEngine&); // 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 create (uInt 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 addRow (uInt nrrow); virtual void addRowInit (uInt startRow, uInt 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 (uInt rownr, const IPosition& shape); // Test if the (underlying) array is defined in the given row. virtual Bool isShapeDefined (uInt 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 (uInt 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 (uInt 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 (uInt rownr, Array& array); // Put an array in the given row. // This will scale and offset to the underlying array. virtual void putArray (uInt 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 (uInt 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 (uInt 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 (uInt 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: // Assignment is not needed and therefore forbidden // (so it is made private and not implemented). BaseMappedArrayEngine& operator= (const BaseMappedArrayEngine&); //# 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 uInt 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-2.4.1/tables/DataMan/BaseMappedArrayEngine.tcc000066400000000000000000000331751321422335000227020ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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::create (uInt 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::addRow (uInt nrrow) { addRowInit (table().nrow(), nrrow); } template void BaseMappedArrayEngine::addRowInit (uInt startRow, uInt nrrow) { if (arrayIsFixed_p && ((column_p->columnDesc().options() & ColumnDesc::FixedShape) != ColumnDesc::FixedShape)) { for (uInt 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 (uInt rownr, const IPosition& shape) { column_p->setShape (rownr, shape); } template Bool BaseMappedArrayEngine::isShapeDefined (uInt rownr) { return column_p->isDefined (rownr); } template uInt BaseMappedArrayEngine::ndim (uInt rownr) { return column_p->ndim (rownr); } template IPosition BaseMappedArrayEngine::shape (uInt rownr) { return column_p->shape (rownr); } template Bool BaseMappedArrayEngine::canChangeShape() const { return (column_p == 0 ? False : column_p->canChangeShape()); } template void BaseMappedArrayEngine::getArray (uInt rownr, Array& array) { Array target(getStoredShape(0, array.shape())); column().baseGet (rownr, target); mapOnGet (array, target); } template void BaseMappedArrayEngine::putArray (uInt rownr, const Array& array) { Array target(getStoredShape(0, array.shape())); mapOnPut (array, target); column().basePut (rownr, target); } template void BaseMappedArrayEngine::getSlice (uInt 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 (uInt 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 (uInt, 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-2.4.1/tables/DataMan/BitFlagsEngine.cc000066400000000000000000000060331321422335000212020ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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-2.4.1/tables/DataMan/BitFlagsEngine.h000066400000000000000000000360001321422335000210410ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (uInt 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(); // 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&); // Assignment is not needed and therefore forbidden // (so it is made private and not implemented). BitFlagsEngine& operator= (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 create (uInt 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 (uInt rownr, Array& array); // Put an array in the given row. // This will scale and offset to the underlying array. void putArray (uInt 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 (uInt 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 (uInt 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 : public std::unary_function { 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 : public std::binary_function { 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-2.4.1/tables/DataMan/BitFlagsEngine.tcc000066400000000000000000000242641321422335000213740ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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::create (uInt initialNrrow) { BaseMappedArrayEngine::create (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 (uInt rownr, Array& array) { Array target(array.shape()); column().get (rownr, target); mapOnGet (array, target); } template void BitFlagsEngine::putArray (uInt rownr, const Array& array) { Array target(array.shape()); mapOnPut (array, target); column().put (rownr, target); } template void BitFlagsEngine::getSlice (uInt rownr, const Slicer& slicer, Array& array) { Array target(array.shape()); column().getSlice (rownr, slicer, target); mapOnGet (array, target); } template void BitFlagsEngine::putSlice (uInt 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-2.4.1/tables/DataMan/CompressComplex.cc000066400000000000000000000574341321422335000215170ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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::create (uInt initialNrrow) { BaseMappedArrayEngine::create (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 (uInt startRow, uInt nrrow) { BaseMappedArrayEngine::addRowInit (startRow, nrrow); if (autoScale_p) { for (uInt 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 uInt nr = array.nelements(); Bool firstTime = True; for (uInt 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 uInt nr = array.nelements(); for (uInt 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 uInt nr = array.nelements(); for (uInt 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); uInt 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); uInt rownr = 0; while (! arrayIter.pastEnd()) { scaleOnPut (getScale(rownr), getOffset(rownr), arrayIter.array(), targetIter.array()); rownr++; arrayIter.next(); targetIter.next(); } } } void CompressComplex::getArray (uInt 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 (uInt 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 (uInt 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 (uInt 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 (uInt 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 (uInt 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); uInt nrrow = table().nrow(); for (uInt rownr=0; rownr& array) { ArrayIterator arrIter(array, array.ndim()-1); RefRowsSliceIter rowsIter(rownrs); while (! rowsIter.pastEnd()) { uInt rownr = rowsIter.sliceStart(); uInt end = rowsIter.sliceEnd(); uInt 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()) { uInt rownr = rowsIter.sliceStart(); uInt end = rowsIter.sliceEnd(); uInt 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); uInt nrrow = table().nrow(); for (uInt rownr=0; rownr& array) { ArrayIterator arrIter(array, array.ndim()-1); RefRowsSliceIter rowsIter(rownrs); while (! rowsIter.pastEnd()) { uInt rownr = rowsIter.sliceStart(); uInt end = rowsIter.sliceEnd(); uInt 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()) { uInt rownr = rowsIter.sliceStart(); uInt end = rowsIter.sliceEnd(); uInt 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::create (uInt initialNrrow) { CompressComplex::create (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 uInt nr = array.nelements(); Bool firstTime = True; for (uInt 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 uInt nr = array.nelements(); for (uInt 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 uInt nr = array.nelements(); for (uInt i=0; i 32768*32768-1) { s = 32768*32768-1; } else { s = Int(f); } } // 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-2.4.1/tables/DataMan/CompressComplex.h000066400000000000000000000514571321422335000213600ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (uInt 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(); // 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 private). CompressComplex (const CompressComplex&); private: // Assignment is not needed and therefore forbidden // (so it is made private and not implemented). CompressComplex& operator= (const CompressComplex&); // 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 create (uInt 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 (uInt startRow, uInt nrrow); // Get an array in the given row. // This will scale and offset from the underlying array. virtual void getArray (uInt rownr, Array& array); // Put an array in the given row. // This will scale and offset to the underlying array. virtual void putArray (uInt 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 (uInt 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 (uInt 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 (uInt rownr); // Get the offset value for this row. Float getOffset (uInt 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 (uInt 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 (uInt 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 (uInt 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(); // 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&); // Assignment is not needed and therefore forbidden // (so it is made private and not implemented). CompressComplexSD& operator= (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 create (uInt 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 (uInt rownr) { return (fixed_p ? scale_p : (*scaleColumn_p)(rownr)); } inline Float CompressComplex::getOffset (uInt rownr) { return (fixed_p ? offset_p : (*offsetColumn_p)(rownr)); } } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/tables/DataMan/CompressFloat.cc000066400000000000000000000417251321422335000211510ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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::create (uInt initialNrrow) { BaseMappedArrayEngine::create (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 (uInt startRow, uInt nrrow) { BaseMappedArrayEngine::addRowInit (startRow, nrrow); if (autoScale_p) { for (uInt 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 uInt nr = array.nelements(); Bool firstTime = True; for (uInt 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 uInt nr = array.nelements(); for (uInt i=0; i& array, Array& target) { Bool deleteIn, deleteOut; const Float* in = array.getStorage (deleteIn); Short* out = target.getStorage (deleteOut); const uInt nr = array.nelements(); for (uInt 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); uInt 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); uInt rownr = 0; while (! arrayIter.pastEnd()) { scaleOnPut (getScale(rownr), getOffset(rownr), arrayIter.array(), targetIter.array()); rownr++; arrayIter.next(); targetIter.next(); } } } void CompressFloat::getArray (uInt 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 (uInt 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 (uInt 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 (uInt 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 (uInt 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 (uInt 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); uInt nrrow = table().nrow(); for (uInt rownr=0; rownr& array) { ArrayIterator arrIter(array, array.ndim()-1); RefRowsSliceIter rowsIter(rownrs); while (! rowsIter.pastEnd()) { uInt rownr = rowsIter.sliceStart(); uInt end = rowsIter.sliceEnd(); uInt 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()) { uInt rownr = rowsIter.sliceStart(); uInt end = rowsIter.sliceEnd(); uInt 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); uInt nrrow = table().nrow(); for (uInt rownr=0; rownr& array) { ArrayIterator arrIter(array, array.ndim()-1); RefRowsSliceIter rowsIter(rownrs); while (! rowsIter.pastEnd()) { uInt rownr = rowsIter.sliceStart(); uInt end = rowsIter.sliceEnd(); uInt 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()) { uInt rownr = rowsIter.sliceStart(); uInt end = rowsIter.sliceEnd(); uInt incr = rowsIter.sliceIncr(); while (rownr <= end) { CompressFloat::putSlice (rownr, slicer, arrIter.array()); arrIter.next(); rownr += incr; } rowsIter++; } } } //# NAMESPACE CASACORE - END casacore-2.4.1/tables/DataMan/CompressFloat.h000066400000000000000000000346341321422335000210140ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (uInt 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(); // 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&); // Assignment is not needed and therefore forbidden // (so it is made private and not implemented). CompressFloat& operator= (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 create (uInt 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 (uInt startRow, uInt nrrow); // Get an array in the given row. // This will scale and offset from the underlying array. virtual void getArray (uInt rownr, Array& array); // Put an array in the given row. // This will scale and offset to the underlying array. virtual void putArray (uInt 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 (uInt 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 (uInt 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 (uInt rownr); // Get the offset value for this row. Float getOffset (uInt 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 (uInt 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 (uInt 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 (uInt rownr) { return (fixed_p ? scale_p : (*scaleColumn_p)(rownr)); } inline Float CompressFloat::getOffset (uInt rownr) { return (fixed_p ? offset_p : (*offsetColumn_p)(rownr)); } } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/tables/DataMan/DataManAccessor.cc000066400000000000000000000047231321422335000213550ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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-2.4.1/tables/DataMan/DataManAccessor.h000066400000000000000000000101231321422335000212060ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/tables/DataMan/DataManError.cc000066400000000000000000000055231321422335000207030ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 () throw() { ; } DataManInternalError::DataManInternalError (const String& str) : DataManError("Internal error: " + str) { ; } DataManInternalError::~DataManInternalError () throw() { ; } DataManUnknownCtor::DataManUnknownCtor (const String& msg) : DataManError(msg) {} DataManUnknownCtor::~DataManUnknownCtor () throw() {} DataManInvDT::DataManInvDT () : DataManError ("Invalid data type") { ; } DataManInvDT::DataManInvDT (const String& name) : DataManError ("Invalid data type when accessing column " + name) { ; } DataManInvDT::~DataManInvDT () throw() { ; } DataManInvOper::DataManInvOper () : DataManError ("Invalid operation") { ; } DataManInvOper::DataManInvOper (const String& s) : DataManError ("Invalid operation: " + s) { ; } DataManInvOper::~DataManInvOper () throw() { ; } DataManUnknownVirtualColumn::DataManUnknownVirtualColumn (const String& columnName, const String& engineName) : DataManError ("column " + columnName + " is unknown to virtual column engine " + engineName) { ; } DataManUnknownVirtualColumn::~DataManUnknownVirtualColumn () throw() { ; } TSMError::TSMError (const String& s) : DataManError ("TiledStMan: " + s) { ; } TSMError::~TSMError () throw() { ; } } //# NAMESPACE CASACORE - END casacore-2.4.1/tables/DataMan/DataManError.h000066400000000000000000000137721321422335000205520ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 () throw(); }; // // 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 () throw(); }; // // 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 () throw(); }; // // 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 () throw(); }; // // 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 () throw(); }; // // 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 () throw(); }; // // 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 () throw(); }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/tables/DataMan/DataManInfo.cc000066400000000000000000000214551321422335000205070ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# Includes #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN void DataManInfo::removeHypercolumns (TableDesc& tabDesc) { tabDesc.adjustHypercolumns (SimpleOrderedMap(String())); } void DataManInfo::adjustDesc (TableDesc& tdesc, const Record& dminfo) { // Find out the columns and data manager groups of the fields. SimpleOrderedMap dmTypeMap("", tdesc.ncolumn()); SimpleOrderedMap dmGroupMap("", tdesc.ncolumn()); for (uInt i=0; i cols = sub.asArrayString ("COLUMNS"); for (uInt j=0; jempty()) { cdesc.dataManagerType() = *v; } } v = dmGroupMap.isDefined (name); if (v) { 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; } Vector 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); } } } //# NAMESPACE CASACORE - END casacore-2.4.1/tables/DataMan/DataManInfo.h000066400000000000000000000111751321422335000203470ustar00rootroot00000000000000//# DataManInfo.h: 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef TABLES_DATAMANINFO_H #define TABLES_DATAMANINFO_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations. class TableDesc; 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 table // descriptions. // Often an existing table description and datamanager info record is 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. //
        // // Such things might be necessary in a number //
        //# //# 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); // 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 columns from the dminfo record and return a vector with 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 // have 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); }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/tables/DataMan/DataManager.cc000066400000000000000000000462421321422335000205330ustar00rootroot00000000000000//# DataManager.cc: Storage manager 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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 // for sprintf namespace casacore { //# NAMESPACE CASACORE - BEGIN DataManager::DataManager() : nrcol_p (0), seqnr_p (0), asBigEndian_p (False), tsmOption_p (TSMOption::Buffer, 0, 0), multiFile_p (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; } uInt DataManager::open1 (uInt nrrow, AipsIO& ios) { open (nrrow, ios); return nrrow; } uInt DataManager::resync1 (uInt nrrow) { resync (nrrow); return nrrow; } void DataManager::reopenRW() {} void DataManager::setMaximumCacheSize (uInt) {} void DataManager::showCacheStatistics (ostream&) const {} void DataManager::setTsmOption (const TSMOption& tsmOption) { AlwaysAssert (multiFile_p==0, AipsError); tsmOption_p = tsmOption; } void DataManager::setMultiFile (MultiFileBase* 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]; sprintf (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]; sprintf (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::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. SimpleOrderedMap DataManager::theirRegisterMap (DataManager::unknownDataManager); MutexedInit DataManager::theirMutexedInit(doRegisterMainCtor, 0, Mutex::Recursive); //# Register a mapping. void DataManager::registerCtor (const String& type, DataManagerCtor func) { ScopedMutexLock lock(theirMutexedInit.mutex()); unlockedRegisterCtor (type, func); } //# Test if the data manager is registered. Bool DataManager::isRegistered (const String& type) { ScopedMutexLock lock(theirMutexedInit.mutex()); if (theirRegisterMap.isDefined(type)) { return True; } return False; } //# Get a data manager constructor. //# Return default function if the data manager is undefined. DataManagerCtor DataManager::getCtor (const String& type) { ScopedMutexLock lock(theirMutexedInit.mutex()); DataManagerCtor* fp = theirRegisterMap.isDefined (type); if (fp) { return *fp; } // 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. fp = theirRegisterMap.isDefined (type); if (fp) { return *fp; } } 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 0; } 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 (uInt, const IPosition&) { throw DataManInvOper("setShape only allowed for non-FixedShape arrays" " in column " + columnName()); } void DataManagerColumn::setShapeTiled (uInt rownr, const IPosition& shape, const IPosition&) { setShape (rownr, shape); } // By default the shape is defined (for scalars). Bool DataManagerColumn::isShapeDefined (uInt) { return True; } // The default implementation of ndim is to use the shape. uInt DataManagerColumn::ndim (uInt rownr) { return shape(rownr).nelements(); } // The shape of the array in the given row. IPosition DataManagerColumn::shape (uInt) { return IPosition(0); } // The tile shape of the array in the given row. IPosition DataManagerColumn::tileShape (uInt) { return IPosition(0); } Bool DataManagerColumn::canChangeShape() const { return False; } Bool DataManagerColumn::canAccessScalarColumn (Bool& reask) const { reask = False; return False; } Bool DataManagerColumn::canAccessArrayColumn (Bool& reask) const { reask = False; return False; } Bool DataManagerColumn::canAccessScalarColumnCells (Bool& reask) const { reask = False; return False; } Bool DataManagerColumn::canAccessArrayColumnCells (Bool& reask) const { reask = False; return False; } Bool DataManagerColumn::canAccessSlice (Bool& reask) const { reask = False; return False; } Bool DataManagerColumn::canAccessColumnSlice (Bool& reask) const { reask = False; 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())); } #define DATAMANAGER_GETPUT(T,NM) \ void DataManagerColumn::aips_name2(get,NM) (uInt, T*) \ { throwGet(); } \ void DataManagerColumn::aips_name2(put,NM) (uInt, const T*) \ { throwPut(); } DATAMANAGER_GETPUT(Bool,BoolV) DATAMANAGER_GETPUT(uChar,uCharV) DATAMANAGER_GETPUT(Short,ShortV) DATAMANAGER_GETPUT(uShort,uShortV) DATAMANAGER_GETPUT(Int,IntV) DATAMANAGER_GETPUT(uInt,uIntV) DATAMANAGER_GETPUT(float,floatV) DATAMANAGER_GETPUT(double,doubleV) DATAMANAGER_GETPUT(Complex,ComplexV) DATAMANAGER_GETPUT(DComplex,DComplexV) DATAMANAGER_GETPUT(String,StringV) void DataManagerColumn::getOtherV (uInt, void*) { throw (DataManInvOper ("DataManagerColumn::getOtherV not allowed" " in column " + columnName())); } void DataManagerColumn::putOtherV (uInt, const void*) { throw (DataManInvOper ("DataManagerColumn::putOtherV not allowed" " in column " + columnName())); } void DataManagerColumn::getScalarColumnV (void*) { throw (DataManInvOper("DataManagerColumn::getScalarColumn not allowed" " in column " + columnName())); } void DataManagerColumn::putScalarColumnV (const void*) { throw (DataManInvOper("DataManagerColumn::putScalarColumn not allowed" " in column " + columnName())); } void DataManagerColumn::getScalarColumnCellsV (const RefRows&, void*) { throw (DataManInvOper("DataManagerColumn::getScalarColumnCells not allowed" " in column " + columnName())); } void DataManagerColumn::putScalarColumnCellsV (const RefRows&, const void*) { throw (DataManInvOper("DataManagerColumn::putScalarColumnCells not allowed" " in column " + columnName())); } uInt DataManagerColumn::getBlockV (uInt, uInt, void*) { throw (DataManInvOper("DataManagerColumn::getBlock not allowed" " in column " + columnName())); return 0; } void DataManagerColumn::putBlockV (uInt, uInt, const void*) { throw (DataManInvOper("DataManagerColumn::putBlock not allowed" " in column " + columnName())); } void DataManagerColumn::getArrayV (uInt, void*) { throw (DataManInvOper("DataManagerColumn::getArray not allowed" " in column " + columnName())); } void DataManagerColumn::putArrayV (uInt, const void*) { throw (DataManInvOper("DataManagerColumn::putArray not allowed" " in column " + columnName())); } void DataManagerColumn::getArrayColumnV (void*) { throw (DataManInvOper("DataManagerColumn::getArrayColumn not allowed" " in column " + columnName())); } void DataManagerColumn::putArrayColumnV (const void*) { throw (DataManInvOper("DataManagerColumn::putArrayColumn not allowed" " in column " + columnName())); } void DataManagerColumn::getArrayColumnCellsV (const RefRows&, void*) { throw (DataManInvOper("DataManagerColumn::getArrayColumnCells not allowed" " in column " + columnName())); } void DataManagerColumn::putArrayColumnCellsV (const RefRows&, const void*) { throw (DataManInvOper("DataManagerColumn::putArrayColumnCells not allowed" " in column " + columnName())); } void DataManagerColumn::getSliceV (uInt, const Slicer&, void*) { throw (DataManInvOper("DataManagerColumn::getSlice not allowed" " in column " + columnName())); } void DataManagerColumn::putSliceV (uInt, const Slicer&, const void*) { throw (DataManInvOper("DataManagerColumn::putSlice not allowed" " in column " + columnName())); } void DataManagerColumn::getColumnSliceV (const Slicer&, void*) { throw (DataManInvOper("DataManagerColumn::getColumnSlice not allowed" " in column " + columnName())); } void DataManagerColumn::putColumnSliceV (const Slicer&, const void*) { throw (DataManInvOper("DataManagerColumn::putColumnSlice not allowed" " in column " + columnName())); } void DataManagerColumn::getColumnSliceCellsV (const RefRows&, const Slicer&, void*) { throw (DataManInvOper("DataManagerColumn::getColumnSliceCells not allowed" " in column " + columnName())); } void DataManagerColumn::putColumnSliceCellsV (const RefRows&, const Slicer&, const void*) { throw (DataManInvOper("DataManagerColumn::putColumnSliceCells not allowed" " in column " + columnName())); } //# 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. void DataManager::doRegisterMainCtor (void*) { unlockedRegisterCtor ("StManAipsIO", StManAipsIO::makeObject); unlockedRegisterCtor ("StandardStMan", StandardStMan::makeObject); unlockedRegisterCtor ("IncrementalStMan", IncrementalStMan::makeObject); unlockedRegisterCtor ("TiledDataStMan", TiledDataStMan::makeObject); unlockedRegisterCtor ("TiledCellStMan", TiledCellStMan::makeObject); unlockedRegisterCtor ("TiledColumnStMan", TiledColumnStMan::makeObject); unlockedRegisterCtor ("TiledShapeStMan", TiledShapeStMan::makeObject); unlockedRegisterCtor ("MemoryStMan", MemoryStMan::makeObject); unlockedRegisterCtor (CompressFloat::className(), CompressFloat::makeObject); unlockedRegisterCtor (CompressComplex::className(), CompressComplex::makeObject); unlockedRegisterCtor (CompressComplexSD::className(), CompressComplexSD::makeObject); unlockedRegisterCtor (MappedArrayEngine::className(), MappedArrayEngine::makeObject); unlockedRegisterCtor (ForwardColumnEngine::className(), ForwardColumnEngine::makeObject); unlockedRegisterCtor (VirtualTaQLColumn::className(), VirtualTaQLColumn::makeObject); unlockedRegisterCtor (BitFlagsEngine::className(), BitFlagsEngine::makeObject); unlockedRegisterCtor (BitFlagsEngine::className(), BitFlagsEngine::makeObject); unlockedRegisterCtor (BitFlagsEngine::className(), BitFlagsEngine::makeObject); } } //# NAMESPACE CASACORE - END casacore-2.4.1/tables/DataMan/DataManager.h000066400000000000000000001272331321422335000203750ustar00rootroot00000000000000//# DataManager.h: Abstract base classes for a data manager //# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 DataManagerColumn; class SetupNewTable; class Table; class MultiFileBase; class Record; class IPosition; class Slicer; class RefRows; template class Array; 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. // 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(); // 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). MultiFileBase* 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 nbytes); // 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 (MultiFileBase* 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; MultiFileBase* multiFile_p; //# MultiFile to use; 0=no MultiFile Table* table_p; //# Table this data manager belongs to mutable DataManager* clone_p; //# Pointer to clone (used by SetupNewTab) // The copy constructor cannot be used for this base class. // The clone function should be used instead. // The private declaration of this constructor makes it unusable. DataManager (const DataManager&); // Assignment cannot be used for this base class. // The private declaration of this operator makes it unusable. DataManager& operator= (const DataManager&); // 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 throws a "not possible" exception. virtual void addRow (uInt nrrow); // Delete a row from all columns. // The default implementation throws a "not possible" exception. virtual void removeRow (uInt 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. virtual void create (uInt nrrow) = 0; // 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. virtual void open (uInt nrrow, AipsIO& ios) = 0; // Open as above. // The data manager can return 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. //
        By default it calls open and returns nrrow. virtual uInt open1 (uInt 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. virtual void resync (uInt nrrow) = 0; // Resync as above. // The data manager can return 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. //
        By default it calls resync and returns nrrow. virtual uInt resync1 (uInt 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(); // Declare the mapping of the data manager type name to a static // "makeObject" function. static SimpleOrderedMap theirRegisterMap; static MutexedInit theirMutexedInit; 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); // Register the main data managers (if not done yet). // It is fully thread-safe. static void registerMainCtor() { theirMutexedInit.exec(); } // Serve as default function for theirRegisterMap, which catches all // unknown data manager types. // //
      • TableUnknownDataManager // static DataManager* unknownDataManager (const String& dataManagerType, const Record& spec); private: // Register a data manager constructor. static void unlockedRegisterCtor (const String& type, DataManagerCtor func) { theirRegisterMap.define (type, func); } // Do the actual (thread-safe) registration of the main data managers. static void doRegisterMainCtor (void*); }; // // 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. // // // 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(); // 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 (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 (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 (uInt rownr); // Get the dimensionality of the item in the given row. // By default it returns shape(rownr).nelements(). 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 (uInt rownr); // Get the tile shape of the item in the given row. // By default it returns a zero-length IPosition. virtual IPosition tileShape (uInt rownr); // Can the data manager handle chaging the shape of an existing array? // Default is no. virtual Bool canChangeShape() const; // Can the column data manager handle access to a scalar column? // If not, the caller should access the column by looping through // all cells in the column. // Default is no. //
        // The returned reask switch determines if the information is // permanent. False indicates it is permanent; True indicates it // will be reasked for the next get/putColumn. // By default reask is set to False. virtual Bool canAccessScalarColumn (Bool& reask) const; // Can the column data manager handle access to a clooection of cells // in a scalar column? // If not, the caller should access the column cells by looping through // the cells in the column. // Default is no. //
        // The returned reask switch determines if the information is // permanent. False indicates it is permanent; True indicates it // will be reasked for the next get/putColumn. // By default reask is set to False. virtual Bool canAccessScalarColumnCells (Bool& reask) const; // Can the column data manager handle access to a scalar column? // If not, the caller should access the column by looping through // all cells in the column. // Default is no. //
        // The returned reask switch determines if the information is // permanent. False indicates it is permanent; True indicates it // will be reasked for the next get/putColumn. // By default reask is set to False. virtual Bool canAccessArrayColumn (Bool& reask) const; // Can the column data manager handle access to a collection of cells // in an array column? // If not, the caller should access the column cells by looping through // the cells in the column. // Default is no. //
        // The returned reask switch determines if the information is // permanent. False indicates it is permanent; True indicates it // will be reasked for the next get/putColumn. // By default reask is set to False. virtual Bool canAccessArrayColumnCells (Bool& reask) const; // Can the column data manager handle access to a cell slice? // If not, the caller should do slicing itself (by accessing the // entire array and slicing it). // Default is no. //
        // The returned reask switch determines if the information is // permanent. False indicates it is permanent; True indicates it // will be reasked for the next get/putColumn. // By default reask is set to False. virtual Bool canAccessSlice (Bool& reask) const; // Can the column data manager handle access to a column slice? // If not, the caller should access the column slice by looping through // all cell slices in the column. // Default is no. //
        // The returned reask switch determines if the information is // permanent. False indicates it is permanent; True indicates it // will be reasked for the next get/putColumn. // By default reask is set to False. virtual Bool canAccessColumnSlice (Bool& reask) 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 getV equivalent to achieve that a derived templated class //(like 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 (uInt rownr, Bool* dataPtr) { getBoolV (rownr, dataPtr); } void get (uInt rownr, uChar* dataPtr) { getuCharV (rownr, dataPtr); } void get (uInt rownr, Short* dataPtr) { getShortV (rownr, dataPtr); } void get (uInt rownr, uShort* dataPtr) { getuShortV (rownr, dataPtr); } void get (uInt rownr, Int* dataPtr) { getIntV (rownr, dataPtr); } void get (uInt rownr, uInt* dataPtr) { getuIntV (rownr, dataPtr); } void get (uInt rownr, float* dataPtr) { getfloatV (rownr, dataPtr); } void get (uInt rownr, double* dataPtr) { getdoubleV (rownr, dataPtr); } void get (uInt rownr, Complex* dataPtr) { getComplexV (rownr, dataPtr); } void get (uInt rownr, DComplex* dataPtr) { getDComplexV (rownr, dataPtr); } void get (uInt rownr, String* dataPtr) { getStringV (rownr, dataPtr); } // This function is the get for all non-standard data types. void get (uInt rownr, void* dataPtr) { getOtherV (rownr, dataPtr); } // // Put the scalar value into the given row. // These functions are non-virtual and are converted to their // virtual putV equivalent to achieve that a derived templated class //(like 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 (uInt rownr, const Bool* dataPtr) { putBoolV (rownr, dataPtr); } void put (uInt rownr, const uChar* dataPtr) { putuCharV (rownr, dataPtr); } void put (uInt rownr, const Short* dataPtr) { putShortV (rownr, dataPtr); } void put (uInt rownr, const uShort* dataPtr) { putuShortV (rownr, dataPtr); } void put (uInt rownr, const Int* dataPtr) { putIntV (rownr, dataPtr); } void put (uInt rownr, const uInt* dataPtr) { putuIntV (rownr, dataPtr); } void put (uInt rownr, const float* dataPtr) { putfloatV (rownr, dataPtr); } void put (uInt rownr, const double* dataPtr) { putdoubleV (rownr, dataPtr); } void put (uInt rownr, const Complex* dataPtr) { putComplexV (rownr, dataPtr); } void put (uInt rownr, const DComplex* dataPtr) { putDComplexV (rownr, dataPtr); } void put (uInt rownr, const String* dataPtr) { putStringV (rownr, dataPtr); } // This function is the put for all non-standard data types. void put (uInt rownr, const void* dataPtr) { putOtherV (rownr, dataPtr); } // // Get all scalar values in the column. // The argument dataPtr is in fact a Vector*, but a void* // 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 throws an "invalid operation" exception. virtual void getScalarColumnV (void* dataPtr); // Put all scalar values in the column. // The argument dataPtr is in fact a const Vector*, but a const void* // 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 throws an "invalid operation" exception. virtual void putScalarColumnV (const void* dataPtr); // Get some scalar values in the column. // The argument dataPtr is in fact a Vector*, but a void* // 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 throws an "invalid operation" exception. virtual void getScalarColumnCellsV (const RefRows& rownrs, void* dataPtr); // Put some scalar values in the column. // The argument dataPtr is in fact a const Vector*, but a const void* // 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 throws an "invalid operation" exception. virtual void putScalarColumnCellsV (const RefRows& rownrs, const void* dataPtr); // Get scalars from the given row on with a maximum of nrmax values. // It returns the actual number of values got. // This can be used to get an entire column of scalars or to get // a part of a column (for a cache for example). // The argument dataPtr is in fact a T*, but a void* // is needed to be generic. // The default implementation throws an "invalid operation" exception. virtual uInt getBlockV (uInt rownr, uInt nrmax, void* dataPtr); // Put nrmax scalars from the given row on. // It returns the actual number of values put. // This can be used to put an entire column of scalars or to put // a part of a column (for a cache for example). // The argument dataPtr is in fact a const T*, but a const void* // is needed to be generic. // The default implementation throws an "invalid operation" exception. virtual void putBlockV (uInt rownr, uInt nrmax, const void* dataPtr); // Get the array value in the given row. // The argument dataPtr is in fact an Array*, but a void* // 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 throws an "invalid operation" exception. virtual void getArrayV (uInt rownr, void* dataPtr); // Put the array value into the given row. // The argument dataPtr is in fact a const Array*, but a const void* // 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 throws an "invalid operation" exception. virtual void putArrayV (uInt rownr, const void* dataPtr); // Get all array values in the column. // The argument dataPtr is in fact an Array*, but a void* // 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 throws an "invalid operation" exception. virtual void getArrayColumnV (void* dataPtr); // Put all array values in the column. // The argument dataPtr is in fact a const Array*, but a const void* // 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 throws an "invalid operation" exception. virtual void putArrayColumnV (const void* dataPtr); // Get some array values in the column. // The argument dataPtr is in fact an Array*, but a void* // 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 throws an "invalid operation" exception. virtual void getArrayColumnCellsV (const RefRows& rownrs, void* dataPtr); // Put some array values in the column. // The argument dataPtr is in fact an const Array*, but a const void* // 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 throws an "invalid operation" exception. virtual void putArrayColumnCellsV (const RefRows& rownrs, const void* dataPtr); // Get a section of the array in the given row. // The argument dataPtr is in fact an Array*, but a void* // 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 throws an "invalid operation" exception. virtual void getSliceV (uInt rownr, const Slicer& slicer, void* dataPtr); // Put into a section of the array in the given row. // The argument dataPtr is in fact a const Array*, but a const void* // 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 throws an "invalid operation" exception. virtual void putSliceV (uInt rownr, const Slicer& slicer, const void* dataPtr); // Get a section of all arrays in the column. // The argument dataPtr is in fact an Array*, but a void* // 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 throws an "invalid operation" exception. virtual void getColumnSliceV (const Slicer& slicer, void* dataPtr); // Put into a section of all arrays in the column. // The argument dataPtr is in fact a const Array*, but a const void* // 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 throws an "invalid operation" exception. virtual void putColumnSliceV (const Slicer& slicer, const void* dataPtr); // Get a section of some arrays in the column. // The argument dataPtr is in fact an Array*, but a void* // 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 throws an "invalid operation" exception. virtual void getColumnSliceCellsV (const RefRows& rownrs, const Slicer& slicer, void* dataPtr); // Put into a section of some arrays in the column. // The argument dataPtr is in fact a const Array*, but a const void* // 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 throws an "invalid operation" exception. virtual void putColumnSliceCellsV (const RefRows& rownrs, const Slicer& slicer, const void* dataPtr); // 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 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); // This function is the get for all non-standard data types. virtual void getOtherV (uInt rownr, void* dataPtr); // // Put the scalar value into the given row. // The default implementation throws an "invalid operation" exception. // 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); // This function is the put for all non-standard data types. virtual void putOtherV (uInt rownr, const void* dataPtr); // private: Bool isFixedShape_p; String colName_p; ColumnCache colCache_p; // 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); // The copy constructor cannot be used for this base class. // The private declaration of this constructor makes it unusable. DataManagerColumn (const DataManagerColumn&); // Assignment cannot be used for this base class. // The private declaration of this operator makes it unusable. DataManagerColumn& operator= (const DataManagerColumn&); }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/tables/DataMan/ForwardCol.cc000066400000000000000000000352311321422335000204250ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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::create (uInt) { 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 (uInt rownr, const IPosition& shape) { colPtr_p->setShape (rownr, shape); } uInt ForwardColumn::ndim (uInt rownr) { return colPtr_p->ndim (rownr); } IPosition ForwardColumn::shape(uInt rownr) { return colPtr_p->shape (rownr); } Bool ForwardColumn::isShapeDefined (uInt rownr) { return colPtr_p->isDefined (rownr); } Bool ForwardColumn::canChangeShape() const { return (colPtr_p == 0 ? False : colPtr_p->canChangeShape()); } Bool ForwardColumn::canAccessScalarColumn (Bool& reask) const { return colPtr_p->canAccessScalarColumn (reask); } Bool ForwardColumn::canAccessArrayColumn (Bool& reask) const { return colPtr_p->canAccessArrayColumn (reask); } Bool ForwardColumn::canAccessSlice (Bool& reask) const { return colPtr_p->canAccessSlice (reask); } Bool ForwardColumn::canAccessColumnSlice (Bool& reask) const { return colPtr_p->canAccessColumnSlice (reask); } void ForwardColumn::getArrayV (uInt rownr, void* dataPtr) { colPtr_p->get (rownr, dataPtr); } void ForwardColumn::getSliceV (uInt rownr, const Slicer& ns, void* dataPtr) { colPtr_p->getSlice (rownr, ns, dataPtr); } void ForwardColumn::getScalarColumnV (void* dataPtr) { colPtr_p->getScalarColumn (dataPtr); } void ForwardColumn::getArrayColumnV (void* dataPtr) { colPtr_p->getArrayColumn (dataPtr); } void ForwardColumn::getScalarColumnCellsV (const RefRows& rownrs, void* dataPtr) { colPtr_p->getScalarColumnCells (rownrs, dataPtr); } void ForwardColumn::getArrayColumnCellsV (const RefRows& rownrs, void* dataPtr) { colPtr_p->getArrayColumnCells (rownrs, dataPtr); } void ForwardColumn::getColumnSliceV (const Slicer& ns, void* dataPtr) { colPtr_p->getColumnSlice (ns, dataPtr); } void ForwardColumn::getColumnSliceCellsV (const RefRows& rownrs, const Slicer& ns, void* dataPtr) { colPtr_p->getColumnSliceCells (rownrs, ns, dataPtr); } void ForwardColumn::putArrayV (uInt rownr, const void* dataPtr) { colPtr_p->put (rownr, dataPtr); } void ForwardColumn::putSliceV (uInt rownr, const Slicer& ns, const void* dataPtr) { colPtr_p->putSlice (rownr, ns, dataPtr); } void ForwardColumn::putScalarColumnV (const void* dataPtr) { colPtr_p->putScalarColumn (dataPtr); } void ForwardColumn::putArrayColumnV (const void* dataPtr) { colPtr_p->putArrayColumn (dataPtr); } void ForwardColumn::putScalarColumnCellsV (const RefRows& rownrs, const void* dataPtr) { colPtr_p->putScalarColumnCells (rownrs, dataPtr); } void ForwardColumn::putArrayColumnCellsV (const RefRows& rownrs, const void* dataPtr) { colPtr_p->putArrayColumnCells (rownrs, dataPtr); } void ForwardColumn::putColumnSliceV (const Slicer& ns, const void* dataPtr) { colPtr_p->putColumnSlice (ns, dataPtr); } void ForwardColumn::putColumnSliceCellsV (const RefRows& rownrs, const Slicer& ns, const void* dataPtr) { colPtr_p->putColumnSliceCells (rownrs, ns, dataPtr); } #define FORWARDCOLUMN_GETPUT(T,NM) \ void ForwardColumn::aips_name2(get,NM) (uInt rownr, T* dataPtr) \ { colPtr_p->get (rownr, dataPtr); } \ void ForwardColumn::aips_name2(put,NM) (uInt rownr, const T* dataPtr) \ { colPtr_p->put (rownr, dataPtr); } FORWARDCOLUMN_GETPUT(Bool,BoolV) FORWARDCOLUMN_GETPUT(uChar,uCharV) FORWARDCOLUMN_GETPUT(Short,ShortV) FORWARDCOLUMN_GETPUT(uShort,uShortV) FORWARDCOLUMN_GETPUT(Int,IntV) FORWARDCOLUMN_GETPUT(uInt,uIntV) FORWARDCOLUMN_GETPUT(float,floatV) FORWARDCOLUMN_GETPUT(double,doubleV) FORWARDCOLUMN_GETPUT(Complex,ComplexV) FORWARDCOLUMN_GETPUT(DComplex,DComplexV) FORWARDCOLUMN_GETPUT(String,StringV) FORWARDCOLUMN_GETPUT(void,OtherV) } //# NAMESPACE CASACORE - END casacore-2.4.1/tables/DataMan/ForwardCol.h000066400000000000000000000537511321422335000202760ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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(); // 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: // Copy constructor is not needed and therefore forbidden // (so make it private). ForwardColumn (const ForwardColumn&); // Assignment is not needed and therefore forbidden (so make it private). ForwardColumn& operator= (const ForwardColumn&); // 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; // This data manager may be able to do get/putScalarColumn. Bool canAccessScalarColumn (Bool& reask) const; // This data manager may be able to do get/putArrayColumn. Bool canAccessArrayColumn (Bool& reask) const; // This data manager may be able to do get/putSlice. Bool canAccessSlice (Bool& reask) const; // This data manager may be able to do get/putColumnSlice. Bool canAccessColumnSlice (Bool& reask) 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 (uInt rownr, const IPosition& shape); // Is the value shape defined in the given row? Bool isShapeDefined (uInt rownr); // Get the dimensionality of the item in the given row. uInt ndim (uInt rownr); // Get the shape of the item in the given row. IPosition shape (uInt rownr); // Get the scalar value with a standard data type in the given row. // void getBoolV (uInt rownr, Bool* dataPtr); void getuCharV (uInt rownr, uChar* dataPtr); void getShortV (uInt rownr, Short* dataPtr); void getuShortV (uInt rownr, uShort* dataPtr); void getIntV (uInt rownr, Int* dataPtr); void getuIntV (uInt rownr, uInt* dataPtr); void getfloatV (uInt rownr, float* dataPtr); void getdoubleV (uInt rownr, double* dataPtr); void getComplexV (uInt rownr, Complex* dataPtr); void getDComplexV (uInt rownr, DComplex* dataPtr); void getStringV (uInt rownr, String* dataPtr); // // Get the scalar value with a non-standard data type in the given row. void getOtherV (uInt rownr, void* dataPtr); // Put the scalar value with a standard data type into the given row. // void putBoolV (uInt rownr, const Bool* dataPtr); void putuCharV (uInt rownr, const uChar* dataPtr); void putShortV (uInt rownr, const Short* dataPtr); void putuShortV (uInt rownr, const uShort* dataPtr); void putIntV (uInt rownr, const Int* dataPtr); void putuIntV (uInt rownr, const uInt* dataPtr); void putfloatV (uInt rownr, const float* dataPtr); void putdoubleV (uInt rownr, const double* dataPtr); void putComplexV (uInt rownr, const Complex* dataPtr); void putDComplexV (uInt rownr, const DComplex* dataPtr); void putStringV (uInt rownr, const String* dataPtr); // // Put the scalar value with a non-standard data type into the given row. void putOtherV (uInt rownr, const void* dataPtr); // Get all scalar values in the column. // The argument dataPtr is in fact a Vector*, but a void* // 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 (void* dataPtr); // Put all scalar values in the column. // The argument dataPtr is in fact a const Vector*, but a const void* // 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 void* dataPtr); // Get some scalar values in the column. // The argument dataPtr is in fact a Vector*, but a void* // 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, void* dataPtr); // Put some scalar values in the column. // The argument dataPtr is in fact a const Vector*, but a const void* // 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 void* dataPtr); // Get the array value in the given row. // The argument dataPtr is in fact a Array*, but a void* // 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 (uInt rownr, void* dataPtr); // Put the array value into the given row. // The argument dataPtr is in fact a const Array*, but a const void* // 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 (uInt rownr, const void* dataPtr); // Get a section of the array in the given row. // The argument dataPtr is in fact a Array*, but a void* // 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 (uInt rownr, const Slicer& slicer, void* dataPtr); // Put into a section of the array in the given row. // The argument dataPtr is in fact a const Array*, but a const void* // 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 (uInt rownr, const Slicer& slicer, const void* dataPtr); // Get all scalar values in the column. // The argument dataPtr is in fact a Vector*, but a void* // 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 (void* dataPtr); // Put all scalar values in the column. // The argument dataPtr is in fact a const Vector*, but a const void* // 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 void* dataPtr); // Get some array values in the column. // The argument dataPtr is in fact an Array*, but a void* // 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, void* dataPtr); // Put some array values in the column. // The argument dataPtr is in fact an const Array*, but a const void* // 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 void* dataPtr); // Get a section of all arrays in the column. // The argument dataPtr is in fact a Array*, but a void* // 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, void* dataPtr); // Put a section into all arrays in the column. // The argument dataPtr is in fact a const Array*, but a const void* // 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 void* dataPtr); // Get a section of some arrays in the column. // The argument dataPtr is in fact an Array*, but a void* // 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, void* dataPtr); // Put into a section of some arrays in the column. // The argument dataPtr is in fact a const Array*, but a const void* // 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 void* 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(); // 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: // The copy constructor is forbidden (so it is private). ForwardColumnEngine (const ForwardColumnEngine&); // Assignment is forbidden (so it is private). ForwardColumnEngine& operator= (const ForwardColumnEngine&); // 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 addRow (uInt nrrow); // Delete a row from all columns. // This is not doing anything (but needed to override the default). void removeRow (uInt 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 create (uInt 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-2.4.1/tables/DataMan/ForwardColRow.cc000066400000000000000000000201651321422335000211150ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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::create (uInt) { // 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 (uInt, const IPosition&) { throw (DataManInvOper ("setShape not supported by data manager ForwardColumnIndexedRow")); } uInt ForwardColumnIndexedRow::ndim (uInt rownr) { return colPtr()->ndim (convertRownr(rownr)); } IPosition ForwardColumnIndexedRow::shape(uInt rownr) { return colPtr()->shape (convertRownr(rownr)); } Bool ForwardColumnIndexedRow::isShapeDefined (uInt rownr) { return colPtr()->isDefined (convertRownr(rownr)); } Bool ForwardColumnIndexedRow::canChangeShape() const { return False; // put is not supported } Bool ForwardColumnIndexedRow::canAccessScalarColumn (Bool& reask) const { reask = False; return False; } Bool ForwardColumnIndexedRow::canAccessArrayColumn (Bool& reask) const { reask = False; return False; } Bool ForwardColumnIndexedRow::canAccessColumnSlice (Bool& reask) const { reask = False; return False; } void ForwardColumnIndexedRow::getArrayV (uInt rownr, void* dataPtr) { colPtr()->get (convertRownr(rownr), dataPtr); } void ForwardColumnIndexedRow::getSliceV (uInt rownr, const Slicer& ns, void* dataPtr) { colPtr()->getSlice (convertRownr(rownr), ns, dataPtr); } void ForwardColumnIndexedRow::putArrayV (uInt, const void*) { throw (DataManInvOper ("putArray not supported by data manager ForwardColumnIndexedRow")); } void ForwardColumnIndexedRow::putSliceV (uInt, const Slicer&, const void*) { throw (DataManInvOper ("putSlice not supported by data manager ForwardColumnIndexedRow")); } #define FORWARDCOLUMNINDEXEDROW_GETPUT(T,NM) \ void ForwardColumnIndexedRow::aips_name2(get,NM) (uInt rownr, T* dataPtr) \ { colPtr()->get (convertRownr(rownr), dataPtr); } \ void ForwardColumnIndexedRow::aips_name2(put,NM) (uInt, const T*) \ { \ throw (DataManInvOper \ ("put not supported by data manager ForwardColumnIndexedRow")); \ } FORWARDCOLUMNINDEXEDROW_GETPUT(Bool,BoolV) FORWARDCOLUMNINDEXEDROW_GETPUT(uChar,uCharV) FORWARDCOLUMNINDEXEDROW_GETPUT(Short,ShortV) FORWARDCOLUMNINDEXEDROW_GETPUT(uShort,uShortV) FORWARDCOLUMNINDEXEDROW_GETPUT(Int,IntV) FORWARDCOLUMNINDEXEDROW_GETPUT(uInt,uIntV) FORWARDCOLUMNINDEXEDROW_GETPUT(float,floatV) FORWARDCOLUMNINDEXEDROW_GETPUT(double,doubleV) FORWARDCOLUMNINDEXEDROW_GETPUT(Complex,ComplexV) FORWARDCOLUMNINDEXEDROW_GETPUT(DComplex,DComplexV) FORWARDCOLUMNINDEXEDROW_GETPUT(String,StringV) FORWARDCOLUMNINDEXEDROW_GETPUT(void,OtherV) } //# NAMESPACE CASACORE - END casacore-2.4.1/tables/DataMan/ForwardColRow.h000066400000000000000000000371611321422335000207630ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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(); // 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: // Copy constructor is not needed and therefore forbidden // (so make it private). ForwardColumnIndexedRow (const ForwardColumnIndexedRow&); // Assignment is not needed and therefore forbidden (so make it private). ForwardColumnIndexedRow& operator= (const ForwardColumnIndexedRow&); // This data manager cannot handle changing array shapes. Bool canChangeShape() const; // This data manager cannot do get/putColumn. Bool canAccessScalarColumn (Bool& reask) const; // This data manager cannot do get/putColumn. Bool canAccessArrayColumn (Bool& reask) const; // This data manager cannot do get/putColumn. Bool canAccessColumnSlice (Bool& reask) const; // Set the shape of an (indirect) array in the given row. // This throws an exception, because putting is not supported. void setShape (uInt rownr, const IPosition& shape); // Is the value shape defined in the given row? Bool isShapeDefined (uInt rownr); // Get the dimensionality of the item in the given row. uInt ndim (uInt rownr); // Get the shape of the item in the given row. IPosition shape (uInt rownr); // Get the scalar value with a standard data type in the given row. // void getBoolV (uInt rownr, Bool* dataPtr); void getuCharV (uInt rownr, uChar* dataPtr); void getShortV (uInt rownr, Short* dataPtr); void getuShortV (uInt rownr, uShort* dataPtr); void getIntV (uInt rownr, Int* dataPtr); void getuIntV (uInt rownr, uInt* dataPtr); void getfloatV (uInt rownr, float* dataPtr); void getdoubleV (uInt rownr, double* dataPtr); void getComplexV (uInt rownr, Complex* dataPtr); void getDComplexV (uInt rownr, DComplex* dataPtr); void getStringV (uInt rownr, String* dataPtr); // // Get the scalar value with a non-standard data type in the given row. void getOtherV (uInt 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. // void putBoolV (uInt rownr, const Bool* dataPtr); void putuCharV (uInt rownr, const uChar* dataPtr); void putShortV (uInt rownr, const Short* dataPtr); void putuShortV (uInt rownr, const uShort* dataPtr); void putIntV (uInt rownr, const Int* dataPtr); void putuIntV (uInt rownr, const uInt* dataPtr); void putfloatV (uInt rownr, const float* dataPtr); void putdoubleV (uInt rownr, const double* dataPtr); void putComplexV (uInt rownr, const Complex* dataPtr); void putDComplexV (uInt rownr, const DComplex* dataPtr); void putStringV (uInt 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. void putOtherV (uInt rownr, const void* dataPtr); // Get the array value in the given row. // The argument dataPtr is in fact a Array*, but a void* // 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 (uInt rownr, void* dataPtr); // Put the array value into the given row. // This throws an exception, because putting is not supported. void putArrayV (uInt rownr, const void* dataPtr); // Get a section of the array in the given row. // The argument dataPtr is in fact a Array*, but a void* // 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 (uInt rownr, const Slicer& slicer, void* dataPtr); // Put into a section of the array in the given row. // This throws an exception, because putting is not supported. void putSliceV (uInt rownr, const Slicer& slicer, const void* dataPtr); // Convert the rownr to the rownr in the underlying table. uInt convertRownr (uInt 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(); // 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: // The copy constructor is forbidden (so it is private). ForwardColumnIndexedRowEngine (const ForwardColumnIndexedRowEngine&); // Assignment is forbidden (so it is private). ForwardColumnIndexedRowEngine& operator= (const ForwardColumnIndexedRowEngine&); // 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 create (uInt 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. Int lastRow_p; uInt 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. uInt convertRownr (uInt rownr); }; inline uInt ForwardColumnIndexedRowEngine::convertRownr (uInt rownr) { if (Int(rownr) != lastRow_p) { rowNumber_p = rowColumn_p(rownr); lastRow_p = rownr; } return rowNumber_p; } inline uInt ForwardColumnIndexedRow::convertRownr (uInt rownr) { return enginePtr_p->convertRownr (rownr); } } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/tables/DataMan/ISMBase.cc000066400000000000000000000523731321422335000176140ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include #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.asInt ("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 ("ActualCacheSize", Int(cacheSize_p)); return rec; } void ISMBase::setProperties (const Record& rec) { if (rec.isDefined("ActualCacheSize")) { setCacheSize (rec.asInt("ActualCacheSize"), 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; uInt bstrow=0; uInt bnrow, 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); AlwaysAssert (cache_p != 0, AipsError); // Allocate a buffer for temporary storage by all ISM classes. if (tempBuffer_p == 0) { tempBuffer_p = new char [bucketSize_p]; AlwaysAssert (tempBuffer_p != 0, AipsError); } } } void ISMBase::makeIndex() { if (index_p != 0) { return; } index_p = new ISMIndex (this); AlwaysAssert (index_p != 0, AipsError); file_p->open(); readIndex(); } void ISMBase::readIndex() { file_p->seek (0); // Use the file given by the BucketFile object. CountedPtr fio = file_p->makeFilebufIO (1024); TypeIO* tio; // It is stored in canonical or local format. if (asBigEndian()) { tio = new CanonicalIO (fio.get()); }else{ tio = new LECanonicalIO (fio.get()); } 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(); delete tio; } 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. CountedPtr fio = file_p->makeFilebufIO (1024); TypeIO* tio; // Store it in canonical or local format. if (asBigEndian()) { tio = new CanonicalIO (fio.get()); }else{ tio = new LECanonicalIO (fio.get()); } 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(); delete tio; } ISMBucket* ISMBase::getBucket (uInt rownr, uInt& bucketStartRow, uInt& bucketNrrow) { uInt bucketNr = getIndex().getBucketNr (rownr, bucketStartRow, bucketNrrow); return (ISMBucket*) (getCache().getBucket (bucketNr)); } ISMBucket* ISMBase::nextBucket (uInt& cursor, uInt& bucketStartRow, uInt& 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 (uInt 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::addRow (uInt 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::removeRow (uInt rownr) { // Get the bucket and interval to which the row belongs. uInt i; uInt bucketStartRow, 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; } void ISMBase::resync (uInt 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(); } } void ISMBase::create (uInt nrrow) { init(); recreate(); nrrow_p = 0; addRow (nrrow); } void ISMBase::open (uInt 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()); AlwaysAssert (file_p != 0, AipsError); //# 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); } } 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()); // 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; } } } } } Bool ISMBase::checkBucketLayout (uInt &offendingCursor, uInt &offendingBucketStartRow, uInt &offendingBucketNrow, uInt &offendingBucketNr, uInt &offendingCol, uInt &offendingIndex, uInt &offendingRow, uInt &offendingPrevRow) { Bool ok = False; uInt cursor = 0; uInt bucketStartRow = 0; uInt 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-2.4.1/tables/DataMan/ISMBase.h000066400000000000000000000343411321422335000174510ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 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 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(); // 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 ActualCacheSize 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 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 (uInt rownr, uInt& bucketStartRow, uInt& 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, uInt& bucketStartRow, uInt& 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. uInt 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 (uInt 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, uInt &offendingBucketStartRow, uInt &offendingBucketNrow, uInt &offendingBucketNr, uInt &offendingCol, uInt &offendingIndex, uInt &offendingRow, uInt &offendingPrevRow); private: // Copy constructor (only meant for clone function). ISMBase (const ISMBase& that); // Assignment cannot be used. ISMBase& operator= (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 create (uInt nrrow); // Open the storage manager file for an existing table, read in // the data, and let the ISMColumn objects read their data. virtual void open (uInt nrrow, AipsIO&); // Resync the storage manager with the new file contents. // This is done by clearing the cache. virtual void resync (uInt 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 addRow (uInt nrrow); // Delete a row from all columns. virtual void removeRow (uInt 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. uInt 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; // 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 uInt ISMBase::nrow() const { return nrrow_p; } inline uInt ISMBase::bucketSize() const { return bucketSize_p; } inline uInt ISMBase::uIntSize() const { return uIntSize_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-2.4.1/tables/DataMan/ISMBucket.cc000066400000000000000000000470011321422335000201470ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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()), 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 += 2*uIntSize_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 -= 2 * nr * uIntSize_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()); // The index will be written just after the data. uInt offset = dataLeng_p + uIntSize_p; writeuInt (bucketStorage, &offset, 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 (dataLeng_p + indexLeng_p == offset && offset <= stmanPtr_p->bucketSize(), AipsError); } void ISMBucket::read (const char* bucketStorage) { uInt nrcol = stmanPtr_p->ncolumn(); Conversion::ValueFunction* readuInt = ISMColumn::getReaduInt (stmanPtr_p->asBigEndian()); // Get the offset of the index. uInt offset; readuInt (&offset, bucketStorage, 1); // 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. for (uInt i=0; iresize (nr); offIndex_p[i]->resize (nr); offset += readuInt (rowIndex_p[i]->storage(), bucketStorage+offset, nr); offset += readuInt (offIndex_p[i]->storage(), bucketStorage+offset, nr); } // Calculate length of index (in external format). indexLeng_p = offset - dataLeng_p; } Bool ISMBucket::simpleSplit (ISMBucket* left, ISMBucket* right, Block& duplicated, uInt& splitRownr, uInt rownr) { // Determine the last rownr in the bucket. uInt i, row; uInt lastRow = 0; uInt nrcol = stmanPtr_p->ncolumn(); for (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 (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; } uInt ISMBucket::split (ISMBucket*& left, ISMBucket*& right, Block& duplicated, uInt bucketStartRow, uInt bucketNrrow, uInt colnr, uInt rownr, uInt lengToAdd) { uInt nrcol = stmanPtr_p->ncolumn(); duplicated.resize (nrcol); left = new ISMBucket (stmanPtr_p, 0); right = new ISMBucket (stmanPtr_p, 0); uInt 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 i, j; uInt nr = 0; for (i=0; i rows(nr + 1); rows[0] = rownr; // new item nr = 1; for (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 (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 (j=0; j toCursor(nrcol, 1); index++; while (index < nruniq) { row = rows[index]; for (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, uInt 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(); // 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, uInt rownr, uInt bucketNrrow, uInt& start, uInt& 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, uInt 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, uInt 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. uInt split (ISMBucket*& left, ISMBucket*& right, Block& duplicated, uInt bucketStartRow, uInt bucketNrrow, uInt colnr, uInt 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, uInt& splitRownr, uInt 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, uInt &offendingRow, uInt &offendingPrevRow) const; private: // Forbid copy constructor. ISMBucket (const ISMBucket&); // Forbid assignment. ISMBucket& operator= (const ISMBucket&); // 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, uInt 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 (used in index, etc.). uInt uIntSize_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-2.4.1/tables/DataMan/ISMColumn.cc000066400000000000000000001175261321422335000202010ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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) : StManColumn (dataType), stmanPtr_p (parent), fixedLength_p (0), colnr_p (colnr), nrelem_p (1), startRow_p (-1), endRow_p (-1), 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 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; } lastValue_p = 0; } void ISMColumn::setShapeColumn (const IPosition& shape) { nrelem_p = shape.product(); shape_p = shape; } uInt ISMColumn::ndim (uInt) { return shape_p.nelements(); } IPosition ISMColumn::shape (uInt) { return shape_p; } void ISMColumn::addRow (uInt, uInt) { //# Nothing to do. } void ISMColumn::remove (uInt bucketRownr, ISMBucket* bucket, uInt bucketNrrow, uInt newNrrow) { uInt inx, stint, endint, offset; // 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 = -1; // 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::getBoolV (uInt rownr, Bool* value) { if (isLastValueInvalid (rownr)) { getValue (rownr, lastValue_p, True); } *value = *(Bool*)lastValue_p; } void ISMColumn::getuCharV (uInt rownr, uChar* value) { if (isLastValueInvalid (rownr)) { getValue (rownr, lastValue_p, True); } *value = *(uChar*)lastValue_p; } void ISMColumn::getShortV (uInt rownr, Short* value) { if (isLastValueInvalid (rownr)) { getValue (rownr, lastValue_p, True); } *value = *(Short*)lastValue_p; } void ISMColumn::getuShortV (uInt rownr, uShort* value) { if (isLastValueInvalid (rownr)) { getValue (rownr, lastValue_p, True); } *value = *(uShort*)lastValue_p; } void ISMColumn::getIntV (uInt rownr, Int* value) { if (isLastValueInvalid (rownr)) { getValue (rownr, lastValue_p, True); } *value = *(Int*)lastValue_p; } void ISMColumn::getuIntV (uInt rownr, uInt* value) { if (isLastValueInvalid (rownr)) { getValue (rownr, lastValue_p, True); } *value = *(uInt*)lastValue_p; } void ISMColumn::getfloatV (uInt rownr, float* value) { if (isLastValueInvalid (rownr)) { getValue (rownr, lastValue_p, True); } *value = *(float*)lastValue_p; } void ISMColumn::getdoubleV (uInt rownr, double* value) { if (isLastValueInvalid (rownr)) { getValue (rownr, lastValue_p, True); } *value = *(double*)lastValue_p; } void ISMColumn::getComplexV (uInt rownr, Complex* value) { if (isLastValueInvalid (rownr)) { getValue (rownr, lastValue_p, True); } *value = *(Complex*)lastValue_p; } void ISMColumn::getDComplexV (uInt rownr, DComplex* value) { if (isLastValueInvalid (rownr)) { getValue (rownr, lastValue_p, True); } *value = *(DComplex*)lastValue_p; } void ISMColumn::getStringV (uInt rownr, String* value) { if (isLastValueInvalid (rownr)) { getValue (rownr, lastValue_p, True); } *value = *(String*)lastValue_p; } void ISMColumn::getScalarColumnBoolV (Vector* dataPtr) { uInt nrrow = dataPtr->nelements(); uInt rownr = 0; while (rownr < nrrow) { getBoolV (rownr, &((*dataPtr)(rownr))); for (rownr++; Int(rownr)<=endRow_p; rownr++) { (*dataPtr)(rownr) = *(Bool*)lastValue_p; } } } void ISMColumn::getScalarColumnuCharV (Vector* dataPtr) { uInt nrrow = dataPtr->nelements(); uInt rownr = 0; while (rownr < nrrow) { getuCharV (rownr, &((*dataPtr)(rownr))); for (rownr++; Int(rownr)<=endRow_p; rownr++) { (*dataPtr)(rownr) = *(uChar*)lastValue_p; } } } void ISMColumn::getScalarColumnShortV (Vector* dataPtr) { uInt nrrow = dataPtr->nelements(); uInt rownr = 0; while (rownr < nrrow) { getShortV (rownr, &((*dataPtr)(rownr))); for (rownr++; Int(rownr)<=endRow_p; rownr++) { (*dataPtr)(rownr) = *(Short*)lastValue_p; } } } void ISMColumn::getScalarColumnuShortV (Vector* dataPtr) { uInt nrrow = dataPtr->nelements(); uInt rownr = 0; while (rownr < nrrow) { getuShortV (rownr, &((*dataPtr)(rownr))); for (rownr++; Int(rownr)<=endRow_p; rownr++) { (*dataPtr)(rownr) = *(uShort*)lastValue_p; } } } void ISMColumn::getScalarColumnIntV (Vector* dataPtr) { uInt nrrow = dataPtr->nelements(); uInt rownr = 0; while (rownr < nrrow) { getIntV (rownr, &((*dataPtr)(rownr))); for (rownr++; Int(rownr)<=endRow_p; rownr++) { (*dataPtr)(rownr) = *(Int*)lastValue_p; } } } void ISMColumn::getScalarColumnuIntV (Vector* dataPtr) { uInt nrrow = dataPtr->nelements(); uInt rownr = 0; while (rownr < nrrow) { getuIntV (rownr, &((*dataPtr)(rownr))); for (rownr++; Int(rownr)<=endRow_p; rownr++) { (*dataPtr)(rownr) = *(uInt*)lastValue_p; } } } void ISMColumn::getScalarColumnfloatV (Vector* dataPtr) { //# Note: using getStorage/putStorage is about 3 times faster //# if the vector is consecutive, but it is slower if not. uInt nrrow = dataPtr->nelements(); uInt rownr = 0; while (rownr < nrrow) { getfloatV (rownr, &((*dataPtr)(rownr))); for (rownr++; Int(rownr)<=endRow_p; rownr++) { (*dataPtr)(rownr) = *(float*)lastValue_p; } } } void ISMColumn::getScalarColumndoubleV (Vector* dataPtr) { uInt nrrow = dataPtr->nelements(); uInt rownr = 0; while (rownr < nrrow) { getdoubleV (rownr, &((*dataPtr)(rownr))); for (rownr++; Int(rownr)<=endRow_p; rownr++) { (*dataPtr)(rownr) = *(double*)lastValue_p; } } } void ISMColumn::getScalarColumnComplexV (Vector* dataPtr) { uInt nrrow = dataPtr->nelements(); uInt rownr = 0; while (rownr < nrrow) { getComplexV (rownr, &((*dataPtr)(rownr))); for (rownr++; Int(rownr)<=endRow_p; rownr++) { (*dataPtr)(rownr) = *(Complex*)lastValue_p; } } } void ISMColumn::getScalarColumnDComplexV (Vector* dataPtr) { uInt nrrow = dataPtr->nelements(); uInt rownr = 0; while (rownr < nrrow) { getDComplexV (rownr, &((*dataPtr)(rownr))); for (rownr++; Int(rownr)<=endRow_p; rownr++) { (*dataPtr)(rownr) = *(DComplex*)lastValue_p; } } } void ISMColumn::getScalarColumnStringV (Vector* dataPtr) { uInt nrrow = dataPtr->nelements(); uInt rownr = 0; while (rownr < nrrow) { getStringV (rownr, &((*dataPtr)(rownr))); for (rownr++; Int(rownr)<=endRow_p; rownr++) { (*dataPtr)(rownr) = *(String*)lastValue_p; } } } #define ISMCOLUMN_GET(T,NM) \ void ISMColumn::aips_name2(getScalarColumnCells,NM) \ (const RefRows& rownrs, \ Vector* values) \ { \ Bool delV; \ T* value = values->getStorage (delV); \ T* valptr = value; \ const ColumnCache& cache = columnCache(); \ if (rownrs.isSliced()) { \ RefRowsSliceIter iter(rownrs); \ while (! iter.pastEnd()) { \ uInt rownr = iter.sliceStart(); \ uInt end = iter.sliceEnd(); \ uInt incr = iter.sliceIncr(); \ while (rownr <= end) { \ if (rownr < cache.start() || rownr > cache.end()) { \ aips_name2(get,NM) (rownr, valptr); \ DebugAssert (cache.incr() == 0, AipsError); \ } \ const T* cacheValue = (const T*)(cache.dataPtr()); \ uInt endrow = min (end, cache.end()); \ while (rownr <= endrow) { \ *valptr++ = *cacheValue; \ rownr += incr; \ } \ } \ iter++; \ } \ } else { \ const Vector& rowvec = rownrs.rowVector(); \ uInt nr = rowvec.nelements(); \ if (nr > 0) { \ Bool delR; \ const uInt* rows = rowvec.getStorage (delR); \ if (rows[0] < cache.start() || rows[0] > cache.end()) { \ aips_name2(get,NM) (0, &(value[0])); \ } \ const T* cacheValue = (const T*)(cache.dataPtr()); \ uInt strow = cache.start(); \ uInt endrow = cache.end(); \ AlwaysAssert (cache.incr() == 0, AipsError); \ for (uInt i=0; i= strow && rownr <= endrow) { \ value[i] = *cacheValue; \ } else { \ aips_name2(get,NM) (rownr, &(value[i])); \ cacheValue = (const T*)(cache.dataPtr()); \ strow = cache.start(); \ endrow = cache.end(); \ } \ } \ rowvec.freeStorage (rows, delR); \ } \ } \ values->putStorage (value, delV); \ } ISMCOLUMN_GET(Bool,BoolV) ISMCOLUMN_GET(uChar,uCharV) ISMCOLUMN_GET(Short,ShortV) ISMCOLUMN_GET(uShort,uShortV) ISMCOLUMN_GET(Int,IntV) ISMCOLUMN_GET(uInt,uIntV) ISMCOLUMN_GET(float,floatV) ISMCOLUMN_GET(double,doubleV) ISMCOLUMN_GET(Complex,ComplexV) ISMCOLUMN_GET(DComplex,DComplexV) ISMCOLUMN_GET(String,StringV) void ISMColumn::getValue (uInt rownr, void* value, Bool setCache) { // Get the bucket with its row number boundaries. uInt bucketStartRow, bucketNrrow; ISMBucket* bucket = stmanPtr_p->getBucket (rownr, bucketStartRow, bucketNrrow); // Get the interval in the bucket with its rownr boundaries. rownr -= bucketStartRow; uInt offset, 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::putBoolV (uInt rownr, const Bool* value) { putValue (rownr, value); } void ISMColumn::putuCharV (uInt rownr, const uChar* value) { putValue (rownr, value); } void ISMColumn::putShortV (uInt rownr, const Short* value) { putValue (rownr, value); } void ISMColumn::putuShortV (uInt rownr, const uShort* value) { putValue (rownr, value); } void ISMColumn::putIntV (uInt rownr, const Int* value) { putValue (rownr, value); } void ISMColumn::putuIntV (uInt rownr, const uInt* value) { putValue (rownr, value); } void ISMColumn::putfloatV (uInt rownr, const float* value) { putValue (rownr, value); } void ISMColumn::putdoubleV (uInt rownr, const double* value) { putValue (rownr, value); } void ISMColumn::putComplexV (uInt rownr, const Complex* value) { putValue (rownr, value); } void ISMColumn::putDComplexV (uInt rownr, const DComplex* value) { putValue (rownr, value); } void ISMColumn::putStringV (uInt rownr, const String* value) { putValue (rownr, value); } void ISMColumn::putScalarColumnBoolV (const Vector* dataPtr) { uInt nrrow = dataPtr->nelements(); for (uInt i=0; i* dataPtr) { uInt nrrow = dataPtr->nelements(); for (uInt i=0; i* dataPtr) { uInt nrrow = dataPtr->nelements(); for (uInt i=0; i* dataPtr) { uInt nrrow = dataPtr->nelements(); for (uInt i=0; i* dataPtr) { uInt nrrow = dataPtr->nelements(); for (uInt i=0; i* dataPtr) { uInt nrrow = dataPtr->nelements(); for (uInt i=0; i* dataPtr) { uInt nrrow = dataPtr->nelements(); for (uInt i=0; i* dataPtr) { uInt nrrow = dataPtr->nelements(); for (uInt i=0; i* dataPtr) { uInt nrrow = dataPtr->nelements(); for (uInt i=0; i* dataPtr) { uInt nrrow = dataPtr->nelements(); for (uInt i=0; i* dataPtr) { uInt nrrow = dataPtr->nelements(); for (uInt i=0; i* value) { if (isLastValueInvalid (rownr)) { getValue (rownr, lastValue_p, False); } *value = Array (shape_p, (Bool*)lastValue_p, SHARE); } void ISMColumn::putArrayBoolV (uInt rownr, const Array* value) { Bool deleteIt; const Bool* data = value->getStorage (deleteIt); putValue (rownr, data); value->freeStorage (data, deleteIt); } void ISMColumn::getArrayuCharV (uInt rownr, Array* value) { if (isLastValueInvalid (rownr)) { getValue (rownr, lastValue_p, False); } *value = Array (shape_p, (uChar*)lastValue_p, SHARE); } void ISMColumn::putArrayuCharV (uInt rownr, const Array* value) { Bool deleteIt; const uChar* data = value->getStorage (deleteIt); putValue (rownr, data); value->freeStorage (data, deleteIt); } void ISMColumn::getArrayShortV (uInt rownr, Array* value) { if (isLastValueInvalid (rownr)) { getValue (rownr, lastValue_p, False); } *value = Array (shape_p, (Short*)lastValue_p, SHARE); } void ISMColumn::putArrayShortV (uInt rownr, const Array* value) { Bool deleteIt; const Short* data = value->getStorage (deleteIt); putValue (rownr, data); value->freeStorage (data, deleteIt); } void ISMColumn::getArrayuShortV (uInt rownr, Array* value) { if (isLastValueInvalid (rownr)) { getValue (rownr, lastValue_p, False); } *value = Array (shape_p, (uShort*)lastValue_p, SHARE); } void ISMColumn::putArrayuShortV (uInt rownr, const Array* value) { Bool deleteIt; const uShort* data = value->getStorage (deleteIt); putValue (rownr, data); value->freeStorage (data, deleteIt); } void ISMColumn::getArrayIntV (uInt rownr, Array* value) { if (isLastValueInvalid (rownr)) { getValue (rownr, lastValue_p, False); } *value = Array (shape_p, (Int*)lastValue_p, SHARE); } void ISMColumn::putArrayIntV (uInt rownr, const Array* value) { Bool deleteIt; const Int* data = value->getStorage (deleteIt); putValue (rownr, data); value->freeStorage (data, deleteIt); } void ISMColumn::getArrayuIntV (uInt rownr, Array* value) { if (isLastValueInvalid (rownr)) { getValue (rownr, lastValue_p, False); } *value = Array (shape_p, (uInt*)lastValue_p, SHARE); } void ISMColumn::putArrayuIntV (uInt rownr, const Array* value) { Bool deleteIt; const uInt* data = value->getStorage (deleteIt); putValue (rownr, data); value->freeStorage (data, deleteIt); } void ISMColumn::getArrayfloatV (uInt rownr, Array* value) { if (isLastValueInvalid (rownr)) { getValue (rownr, lastValue_p, False); } *value = Array (shape_p, (float*)lastValue_p, SHARE); } void ISMColumn::putArrayfloatV (uInt rownr, const Array* value) { Bool deleteIt; const float* data = value->getStorage (deleteIt); putValue (rownr, data); value->freeStorage (data, deleteIt); } void ISMColumn::getArraydoubleV (uInt rownr, Array* value) { if (isLastValueInvalid (rownr)) { getValue (rownr, lastValue_p, False); } *value = Array (shape_p, (double*)lastValue_p, SHARE); } void ISMColumn::putArraydoubleV (uInt rownr, const Array* value) { Bool deleteIt; const double* data = value->getStorage (deleteIt); putValue (rownr, data); value->freeStorage (data, deleteIt); } void ISMColumn::getArrayComplexV (uInt rownr, Array* value) { if (isLastValueInvalid (rownr)) { getValue (rownr, lastValue_p, False); } *value = Array (shape_p, (Complex*)lastValue_p, SHARE); } void ISMColumn::putArrayComplexV (uInt rownr, const Array* value) { Bool deleteIt; const Complex* data = value->getStorage (deleteIt); putValue (rownr, data); value->freeStorage (data, deleteIt); } void ISMColumn::getArrayDComplexV (uInt rownr, Array* value) { if (isLastValueInvalid (rownr)) { getValue (rownr, lastValue_p, False); } *value = Array (shape_p, (DComplex*)lastValue_p, SHARE); } void ISMColumn::putArrayDComplexV (uInt rownr, const Array* value) { Bool deleteIt; const DComplex* data = value->getStorage (deleteIt); putValue (rownr, data); value->freeStorage (data, deleteIt); } void ISMColumn::getArrayStringV (uInt rownr, Array* value) { if (isLastValueInvalid (rownr)) { getValue (rownr, lastValue_p, False); } *value = Array (shape_p, (String*)lastValue_p, SHARE); } void ISMColumn::putArrayStringV (uInt rownr, const Array* value) { Bool deleteIt; const String* data = value->getStorage (deleteIt); putValue (rownr, data); value->freeStorage (data, deleteIt); } void ISMColumn::putValue (uInt rownr, const void* value) { // Get the bucket and interval to which the row belongs. uInt bucketStartRow, bucketNrrow; ISMBucket* bucket = stmanPtr_p->getBucket (rownr, bucketStartRow, bucketNrrow); uInt bucketRownr = rownr - bucketStartRow; uInt inx, stint, endint, 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 (uInt 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; uInt bucketNrrow; uInt cursor = 0; bucket = 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)) != 0) { #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, uInt bucketStartRow, uInt bucketNrrow, uInt bucketRownr, const char* data, uInt lenData, Bool afterLastRow, Bool canSplit) { // Determine the index. uInt inx, start, end, dum3; 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, uInt bucketStartRow, uInt bucketNrrow, uInt 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; uInt 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, uInt bucketStartRow, uInt bucketNrrow, uInt 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; uInt 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. uInt startRow = bucketStartRow; uInt 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 (uInt rownr, const char*) { cout << " handleCopy for row " << rownr << ", column " << colnr_p << endl; #else void ISMColumn::handleCopy (uInt, const char*) { #endif } #ifdef AIPS_TRACE void ISMColumn::handleRemove (uInt rownr, const char*) { cout << " handleRemove for row " << rownr << ", column " << colnr_p << endl; #else void ISMColumn::handleRemove (uInt, 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 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 (uInt nrrow) { init(); lastRowPut_p = nrrow; } Bool ISMColumn::flush (uInt, Bool) { return False; } void ISMColumn::resync (uInt nrrow) { // Invalidate the last value read. columnCache().invalidate(); startRow_p = -1; endRow_p = -1; 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::getWriteuInt (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-2.4.1/tables/DataMan/ISMColumn.h000066400000000000000000000434171321422335000200400ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 StManColumn { 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(); // 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 (uInt rownr); // Get the shape of the array in the given row. // This is the same for all rows. virtual IPosition shape (uInt 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 (uInt nrrow); // Flush and optionally fsync the data. // This is meant for a derived class. virtual Bool flush (uInt nrrow, Bool fsync); // Resync the storage manager with the new file contents. // It resets the last rownr put. void resync (uInt 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 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 a 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). // 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 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). // 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 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 implementation loops through all rows. // 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 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); // // Get an array value in the given row. // 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 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 an array value in the given row. // 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 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); // // Add (newNrrow-oldNrrow) rows to the column and initialize // the new rows when needed. virtual void addRow (uInt newNrrow, uInt oldNrrow); // Remove the given row in the bucket from the column. void remove (uInt bucketRownr, ISMBucket* bucket, uInt bucketNrrow, uInt newNrrow); // Get the function needed to read/write a uInt 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* getWriteuInt (Bool asCanonical); // // Give a derived class the opportunity to react on the duplication // of a value. It is used by ISMIndColumn. virtual void handleCopy (uInt 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 (uInt 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. int isLastValueInvalid (Int rownr) const; // Get the value for this row. // Set the cache if the flag is set. void getValue (uInt rownr, void* value, Bool setCache); // Put the value for this row. void putValue (uInt 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). Int startRow_p; Int endRow_p; void* lastValue_p; // The last row for which a value has been put. uInt 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: // Forbid copy constructor. ISMColumn (const ISMColumn&); // Forbid assignment. ISMColumn& operator= (const ISMColumn&); // 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 (uInt 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, uInt bucketStartRow, uInt bucketNrrow, uInt 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, uInt bucketStartRow, uInt bucketNrrow, uInt 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, uInt bucketStartRow, uInt bucketNrrow, uInt 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); // }; inline int ISMColumn::isLastValueInvalid (Int rownr) const { 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-2.4.1/tables/DataMan/ISMIndColumn.cc000066400000000000000000000257571321422335000206400ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# Includes #include #include #include #include #include #include #include #include #include #include #include #include // for sprintf 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 (uInt nrrow) { // Initialize and open existing file. init (stmanPtr_p->fileOption()); lastRowPut_p = nrrow; } Bool ISMIndColumn::flush (uInt, Bool fsync) { return iosfile_p->flush (fsync); } void ISMIndColumn::resync (uInt nrrow) { ISMColumn::resync (nrrow); if (stmanPtr_p->version() < 3) { iosfile_p->resync(); } } void ISMIndColumn::reopenRW() { iosfile_p->reopenRW(); } void ISMIndColumn::addRow (uInt, uInt 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 (uInt 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 (uInt 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 (uInt 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 (uInt 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 (uInt 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 (uInt rownr) { return (getArrayPtr(rownr) == 0 ? False : True); } uInt ISMIndColumn::ndim (uInt rownr) { return getShape(rownr)->shape().nelements(); } IPosition ISMIndColumn::shape (uInt rownr) { return getShape(rownr)->shape(); } Bool ISMIndColumn::canChangeShape() const { return (shapeIsFixed_p ? False : True); } Bool ISMIndColumn::canAccessSlice (Bool& reask) const { reask = False; return True; } StIndArray* ISMIndColumn::putArrayPtr (uInt 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::getArrayfloatV (uInt rownr, Array* arr) { getShape(rownr)->getArrayfloatV (*iosfile_p, arr); } void ISMIndColumn::putArrayfloatV (uInt rownr, const Array* arr) { putShape(rownr, arr->shape())->putArrayfloatV (*iosfile_p, arr); } void ISMIndColumn::getSlicefloatV (uInt rownr, const Slicer& ns, Array* arr) { getShape(rownr)->getSlicefloatV (*iosfile_p, ns, arr); } void ISMIndColumn::putSlicefloatV (uInt rownr, const Slicer& ns, const Array* arr) { putShapeSliced(rownr)->putSlicefloatV (*iosfile_p, ns, arr); } #define ISMIndColumn_GETPUT(T,NM) \ void ISMIndColumn::aips_name2(getArray,NM) (uInt rownr, Array* arr) \ { getShape(rownr)->aips_name2(getArray,NM) (*iosfile_p, arr); } \ void ISMIndColumn::aips_name2(putArray,NM) (uInt rownr, const Array* arr) \ { putShape(rownr, arr->shape())->aips_name2(putArray,NM) \ (*iosfile_p, arr); } \ void ISMIndColumn::aips_name2(getSlice,NM) \ (uInt rownr, const Slicer& ns, Array* arr) \ { getShape(rownr)->aips_name2(getSlice,NM) (*iosfile_p, ns, arr); } \ void ISMIndColumn::aips_name2(putSlice,NM) \ (uInt rownr, const Slicer& ns, const Array* arr) \ { putShapeSliced(rownr)->aips_name2(putSlice,NM) (*iosfile_p, ns, arr); } ISMIndColumn_GETPUT(Bool,BoolV) ISMIndColumn_GETPUT(uChar,uCharV) ISMIndColumn_GETPUT(Short,ShortV) ISMIndColumn_GETPUT(uShort,uShortV) ISMIndColumn_GETPUT(Int,IntV) ISMIndColumn_GETPUT(uInt,uIntV) //#//ISMIndColumn_GETPUT(float,floatV) ISMIndColumn_GETPUT(double,doubleV) ISMIndColumn_GETPUT(Complex,ComplexV) ISMIndColumn_GETPUT(DComplex,DComplexV) ISMIndColumn_GETPUT(String,StringV) 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]; sprintf (strc, "i%i", seqnr_p); iosfile_p = new StManArrayFile (stmanPtr_p->fileName() + strc, fileOption, 1, asBigEndian); } } void ISMIndColumn::handleCopy (uInt, const char* value) { Int64 offset; readFunc_p (&offset, value, nrcopy_p); if (offset != 0) { StIndArray tmp (offset); tmp.incrementRefCount (*iosfile_p); } } void ISMIndColumn::handleRemove (uInt, 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-2.4.1/tables/DataMan/ISMIndColumn.h000066400000000000000000000304341321422335000204660ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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. ~ISMIndColumn(); // It can handle access to a slice in a cell. virtual Bool canAccessSlice (Bool& reask) const; // Add (newNrrow-oldNrrow) rows to the column. virtual void addRow (uInt newNrrow, uInt 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 (uInt rownr); // Set the shape of the array in the given row and allocate the array // in the file. void setShape (uInt rownr, const IPosition& shape); // Is the shape defined (i.e. is there an array) in this row? virtual Bool isShapeDefined (uInt rownr); // Get the shape of the array in the given row. virtual IPosition shape (uInt rownr); // 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 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 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 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 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 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 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 getSliceBoolV (uInt rownr, const Slicer&, Array* dataPtr); virtual void getSliceuCharV (uInt rownr, const Slicer&, Array* dataPtr); virtual void getSliceShortV (uInt rownr, const Slicer&, Array* dataPtr); virtual void getSliceuShortV (uInt rownr, const Slicer&, Array* dataPtr); virtual void getSliceIntV (uInt rownr, const Slicer&, Array* dataPtr); virtual void getSliceuIntV (uInt rownr, const Slicer&, Array* dataPtr); virtual void getSlicefloatV (uInt rownr, const Slicer&, Array* dataPtr); virtual void getSlicedoubleV (uInt rownr, const Slicer&, Array* dataPtr); virtual void getSliceComplexV (uInt rownr, const Slicer&, Array* dataPtr); virtual void getSliceDComplexV (uInt rownr, const Slicer&, Array* dataPtr); virtual void getSliceStringV (uInt rownr, const Slicer&, Array* 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 putSliceBoolV (uInt rownr, const Slicer&, const Array* dataPtr); virtual void putSliceuCharV (uInt rownr, const Slicer&, const Array* dataPtr); virtual void putSliceShortV (uInt rownr, const Slicer&, const Array* dataPtr); virtual void putSliceuShortV (uInt rownr, const Slicer&, const Array* dataPtr); virtual void putSliceIntV (uInt rownr, const Slicer&, const Array* dataPtr); virtual void putSliceuIntV (uInt rownr, const Slicer&, const Array* dataPtr); virtual void putSlicefloatV (uInt rownr, const Slicer&, const Array* dataPtr); virtual void putSlicedoubleV (uInt rownr, const Slicer&, const Array* dataPtr); virtual void putSliceComplexV (uInt rownr, const Slicer&, const Array* dataPtr); virtual void putSliceDComplexV (uInt rownr, const Slicer&, const Array* dataPtr); virtual void putSliceStringV (uInt rownr, const Slicer&, const Array* dataPtr); // // Let the column object create its array file. virtual void doCreate (ISMBucket* bucket); // Let the column object open an existing file. virtual void getFile (uInt nrrow); // Flush and optionally fsync the data. virtual Bool flush (uInt nrrow, Bool fsync); // Resync the storage manager with the new file contents. virtual void resync (uInt 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 (uInt rownr, const char* value); // Handle the removal of a value; i.e. decrement its reference count. virtual void handleRemove (uInt rownr, const char* value); private: // Forbid copy constructor. ISMIndColumn (const ISMIndColumn&); // Forbid assignment. ISMIndColumn& operator= (const ISMIndColumn&); // 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 (uInt rownr); // Put the shape for an array being put. // When there are multiple rows in the interval, it will // split the interval. StIndArray* putShape (uInt 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 (uInt rownr); // Return a pointer to the array in the given row (for a get). StIndArray* getArrayPtr (uInt 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 (uInt 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-2.4.1/tables/DataMan/ISMIndex.cc000066400000000000000000000115211321422335000177770ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# Includes #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN ISMIndex::ISMIndex (ISMBase* parent) : stmanPtr_p (parent), nused_p (1), rows_p (2, (uInt)0), bucketNr_p (1, (uInt)0) {} ISMIndex::~ISMIndex() {} void ISMIndex::get (AipsIO& os) { os.getstart ("ISMIndex"); os >> nused_p; getBlock (os, rows_p); getBlock (os, bucketNr_p); os.getend(); } void ISMIndex::put (AipsIO& os) { os.putstart ("ISMIndex", 1); os << nused_p; putBlock (os, rows_p, nused_p + 1); putBlock (os, bucketNr_p, nused_p); os.putend(); } void ISMIndex::addBucketNr (uInt 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 (uInt nrrow) { rows_p[nused_p] += nrrow; } Int ISMIndex::removeRow (uInt 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 (uInt 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 (uInt rownr, uInt& bucketStartRow, uInt& 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, uInt& bucketStartRow, uInt& 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: // Create a ISMIndex object with the given parent for a new table. // It keeps the pointer to its parent (but does not own it). explicit ISMIndex (ISMBase* parent); // The destructor closes the file (if opened). ~ISMIndex(); // Add a row. void addRow (uInt 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 (uInt 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 (uInt rownr, uInt& bucketStartRow, uInt& 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 (uInt 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 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, uInt& bucketStartRow, uInt& bucketNrrow, uInt& bucketNr) const; // Show the index. void show (std::ostream&) const; private: // Forbid copy constructor. ISMIndex (const ISMIndex&); // Forbid assignment. ISMIndex& operator= (const ISMIndex&); // Get the index of the bucket containing the given row. uInt getIndex (uInt rownr) const; //# Declare member variables. // Pointer to the parent storage manager. ISMBase* stmanPtr_p; // 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-2.4.1/tables/DataMan/IncrStManAccessor.cc000066400000000000000000000104151321422335000217010ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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, uInt &offendingBucketStartRow, uInt &offendingBucketNrow, uInt &offendingBucketNr, uInt &offendingCol, uInt &offendingIndex, uInt &offendingRow, uInt &offendingPrevRow) const { Bool ok; ok = dataManPtr_p->checkBucketLayout (offendingCursor, offendingBucketStartRow, offendingBucketNrow, offendingBucketNr, offendingCol, offendingIndex, offendingRow, offendingPrevRow); return ok; } } //# NAMESPACE CASACORE - END casacore-2.4.1/tables/DataMan/IncrStManAccessor.h000066400000000000000000000136621321422335000215520ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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, uInt &offendingBucketStartRow, uInt &offendingBucketNrow, uInt &offendingBucketNr, uInt &offendingCol, uInt &offendingIndex, uInt &offendingRow, uInt &offendingPrevRow) const; private: //# Declare the data members. ISMBase* dataManPtr_p; }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/tables/DataMan/IncrementalStMan.cc000066400000000000000000000033731321422335000215710ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/tables/DataMan/IncrementalStMan.h000066400000000000000000000233551321422335000214350ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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(); private: // Copy constructor cannot be used. IncrementalStMan (const IncrementalStMan& that); // Assignment cannot be used. IncrementalStMan& operator= (const IncrementalStMan& that); }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/tables/DataMan/MSMBase.cc000066400000000000000000000162641321422335000176170ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN MSMBase::MSMBase() : DataManager (), nrrow_p (0), nrrowCreate_p (0), colSet_p (0) {} MSMBase::MSMBase (const String& storageManagerName) : DataManager (), stmanName_p (storageManagerName), nrrow_p (0), nrrowCreate_p (0), colSet_p (0) {} MSMBase::MSMBase (const String& storageManagerName, const Record&) : DataManager (), stmanName_p (storageManagerName), nrrow_p (0), nrrowCreate_p (0), colSet_p (0) {} 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); addRow (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); return; } } throw DataManInternalError ("MSMBase::addColumn, column " + colp->columnName()); } void MSMBase::removeColumn (DataManagerColumn* colp) { for (uInt i=0; icolumnName() + " does not exist"); } void MSMBase::addRow (uInt nr) { //# Add the number of rows to each column. for (uInt i=0; iaddRow (nrrow_p+nr, nrrow_p); } nrrow_p += nr; } void MSMBase::removeRow (uInt rownr) { for (uInt i=0; iremove (rownr); } nrrow_p--; } Bool MSMBase::flush (AipsIO&, Bool) { return False; } void MSMBase::create (uInt 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 a IndArrColumn is turned // into a DirArrColumn. nrrowCreate_p = nrrow; } void MSMBase::open (uInt tabNrrow, AipsIO&) { nrrow_p = tabNrrow; //# Create the required nr of rows and initialize them. for (uInt i=0; idoCreate (tabNrrow); } } void MSMBase::resync (uInt 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) { addRow (nrrow-nrrow_p); } else { uInt nr=nrrow_p-nrrow; for (uInt i=0; i #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&); // ~MSMBase(); // Clone this object. // It does not clone MSMColumn objects possibly used. DataManager* clone() const; // Get the type name of the data manager (i.e. MemoryStMan). String dataManagerType() const; // Get the name given to this storage manager. String dataManagerName() const; // Get the nr of rows in this storage manager. uInt nrow() const { return nrrow_p; } // Does the storage manager allow to add rows? (yes) Bool canAddRow() const; // Does the storage manager allow to delete rows? (yes) Bool canRemoveRow() const; // Does the storage manager allow to add columns? (yes) Bool canAddColumn() const; // Does the storage manager allow to delete columns? (yes) 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 create (uInt nrrow); // Open the storage manager file for an existing table. // It fills the rows with 0 values. virtual void open (uInt 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 void resync (uInt 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. void addRow (uInt nrrow); // Delete a row from all columns. void removeRow (uInt rownr); // 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 MemoryStMan 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 replace an MSMIndColumn object for indirect array with // a fixed shape by an MSMDirColumn object. DataManagerColumn* reallocateColumn (DataManagerColumn* column); // Add a column. void addColumn (DataManagerColumn*); // Delete a column. void removeColumn (DataManagerColumn*); // Name given by user to this storage manager. String stmanName_p; // The number of rows in the columns. uInt nrrow_p; // The number of rows in create(). uInt nrrowCreate_p; // The assembly of all columns. PtrBlock colSet_p; }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/tables/DataMan/MSMColumn.cc000066400000000000000000000324321321422335000201750ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN #define EXTBLSZ 32 MSMColumn::MSMColumn (MSMBase* smptr, int dataType, Bool byPtr) : StManColumn(dataType), stmanPtr_p (smptr), dtype_p (dataType), byPtr_p (byPtr), nralloc_p (0), nrext_p (0), data_p (EXTBLSZ,static_cast(0)), ncum_p (EXTBLSZ,(uInt)0) {} MSMColumn::~MSMColumn() { deleteAll(); } void MSMColumn::doCreate (uInt nrrow) { addRow (nrrow, 0); initData (data_p[1], nrrow); } void MSMColumn::addRow (uInt nrnew, uInt) { //# Extend the column sizes if needed. if (nrnew > nralloc_p) { uInt n = nralloc_p + 4096; if (n < nrnew) { n = nrnew; } resize (n); } } void MSMColumn::resize (uInt 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 (uInt 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; } uInt MSMColumn::nextExt (void*& ext, uInt& extnr, uInt nrmax) const { if (++extnr > nrext_p) { return 0; } ext = data_p[extnr]; uInt n = ncum_p[extnr]; if (n > nrmax) { n = nrmax; } if (n < ncum_p[extnr-1]) { return 0; } return n - ncum_p[extnr-1]; } #define MSMCOLUMN_GETPUT(T,NM) \ void MSMColumn::aips_name2(get,NM) (uInt rownr, T* value) \ { \ uInt extnr = findExt(rownr, True); \ *value = ((T*)(data_p[extnr])) [rownr-ncum_p[extnr-1]]; \ } \ void MSMColumn::aips_name2(put,NM) (uInt rownr, const T* value) \ { \ uInt extnr = findExt(rownr, True); \ ((T*)(data_p[extnr])) [rownr-ncum_p[extnr-1]] = *value; \ } \ uInt MSMColumn::aips_name2(getBlock,NM) (uInt rownr, uInt nrmax, T* value) \ { \ uInt nr; \ uInt extnr = findExt(rownr, True); \ nrmax = min (nrmax, nralloc_p-rownr); \ uInt nrm = nrmax; \ while (nrmax > 0) { \ nr = min (nrmax, ncum_p[extnr]-rownr); \ objcopy (value, ((T*)(data_p[extnr])) +rownr-ncum_p[extnr-1], nr); \ nrmax -= nr; \ value += nr; \ rownr = ncum_p[extnr]; \ extnr++; \ } \ return nrm; \ } \ void MSMColumn::aips_name2(putBlock,NM) (uInt rownr, uInt nrmax, const T* value) \ { \ uInt nr; \ uInt extnr = findExt(rownr, True); \ nrmax = min (nrmax, nralloc_p-rownr); \ while (nrmax > 0) { \ nr = min (nrmax, ncum_p[extnr]-rownr); \ objcopy (((T*)(data_p[extnr])) +rownr-ncum_p[extnr-1], value, nr); \ nrmax -= nr; \ value += nr; \ rownr = ncum_p[extnr]; \ extnr++; \ } \ } \ void MSMColumn::aips_name2(getScalarColumnCells,NM) \ (const RefRows& rownrs, \ Vector* values) \ { \ Bool delV; \ T* value = values->getStorage (delV); \ T* valptr = value; \ const ColumnCache& cache = columnCache(); \ if (rownrs.isSliced()) { \ RefRowsSliceIter iter(rownrs); \ while (! iter.pastEnd()) { \ uInt rownr = iter.sliceStart(); \ uInt end = iter.sliceEnd(); \ uInt incr = iter.sliceIncr(); \ while (rownr <= end) { \ if (rownr < cache.start() || rownr > cache.end()) { \ aips_name2(get,NM) (rownr, valptr); \ } \ uInt inx = rownr - cache.start(); \ const T* cacheValue = (const T*)(cache.dataPtr()) + inx; \ uInt endrow = min (end, cache.end()); \ while (rownr <= endrow) { \ *valptr++ = *cacheValue; \ rownr += incr; \ cacheValue += incr; \ } \ } \ iter++; \ } \ } else { \ const Vector& rowvec = rownrs.rowVector(); \ uInt nr = rowvec.nelements(); \ if (nr > 0) { \ Bool delR; \ const uInt* rows = rowvec.getStorage (delR); \ if (rows[0] < cache.start() || rows[0] > cache.end()) { \ findExt(rows[0], True); \ } \ const T* cacheValue = (const T*)(cache.dataPtr()); \ uInt strow = cache.start(); \ uInt endrow = cache.end(); \ for (uInt i=0; i= strow && rownr <= endrow) { \ value[i] = cacheValue[rownr-strow]; \ } else { \ aips_name2(get,NM) (rownr, &(value[i])); \ cacheValue = (const T*)(cache.dataPtr()); \ strow = cache.start(); \ endrow = cache.end(); \ } \ } \ rowvec.freeStorage (rows, delR); \ } \ } \ values->putStorage (value, delV); \ } MSMCOLUMN_GETPUT(Bool,BoolV) MSMCOLUMN_GETPUT(uChar,uCharV) MSMCOLUMN_GETPUT(Short,ShortV) MSMCOLUMN_GETPUT(uShort,uShortV) MSMCOLUMN_GETPUT(Int,IntV) MSMCOLUMN_GETPUT(uInt,uIntV) MSMCOLUMN_GETPUT(float,floatV) MSMCOLUMN_GETPUT(double,doubleV) MSMCOLUMN_GETPUT(Complex,ComplexV) MSMCOLUMN_GETPUT(DComplex,DComplexV) MSMCOLUMN_GETPUT(String,StringV) void MSMColumn::remove (uInt index) { //# Find the extension. uInt extnr = findExt(index, False); uInt 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 [] (void**)datap; }else{ switch (dtype_p) { case TpBool: delete [] (Bool*)datap; break; case TpUChar: delete [] (uChar*)datap; break; case TpShort: delete [] (Short*)datap; break; case TpUShort: delete [] (uShort*)datap; break; case TpInt: delete [] (Int*)datap; break; case TpUInt: delete [] (uInt*)datap; break; case TpFloat: delete [] (float*)datap; break; case TpDouble: delete [] (double*)datap; break; case TpComplex: delete [] (Complex*)datap; break; case TpDComplex: delete [] (DComplex*)datap; break; case TpString: delete [] (String*)datap; break; default: throw DataManInvDT(); } } datap = 0; } void* MSMColumn::allocData (uInt nrval, Bool byPtr) { void* datap = 0; if (byPtr) { datap = new void*[nrval]; if (datap != 0) { void** dp = (void**)datap; for (uInt i=0; i= nrvalAfter) { return; } if (byPtr_p) { objmove (((void**)dp) + inx, ((void**)dp) + inx+1 ,nrvalAfter-inx); return; } switch (dtype_p) { case TpBool: objmove (((Bool*)dp) + inx, ((Bool*)dp) + inx+1, nrvalAfter-inx); break; case TpUChar: objmove (((uChar*)dp) + inx, ((uChar*)dp) + inx+1, nrvalAfter-inx); break; case TpShort: objmove (((Short*)dp) + inx, ((Short*)dp) + inx+1, nrvalAfter-inx); break; case TpUShort: objmove (((uShort*)dp) + inx, ((uShort*)dp) + inx+1,nrvalAfter-inx); break; case TpInt: objmove (((Int*)dp) + inx, ((Int*)dp) + inx+1, nrvalAfter-inx); break; case TpUInt: objmove (((uInt*)dp) + inx, ((uInt*)dp) + inx+1, nrvalAfter-inx); break; case TpFloat: objmove (((float*)dp) + inx, ((float*)dp) + inx+1, nrvalAfter-inx); break; case TpDouble: objmove (((double*)dp) + inx, ((double*)dp) + inx+1,nrvalAfter-inx); break; case TpComplex: objmove (((Complex*)dp)+inx, ((Complex*)dp)+inx+1, nrvalAfter-inx); break; case TpDComplex: objmove (((DComplex*)dp)+inx, ((DComplex*)dp)+inx+1,nrvalAfter-inx); break; case TpString: objmove (((String*)dp) + inx, ((String*)dp) + inx+1,nrvalAfter-inx); break; default: throw DataManInvDT(); } } void MSMColumn::initData (void* datap, uInt nrval) { // Pointers are already initialized by allocData. if (!byPtr_p) { switch (dtype_p) { case TpBool: objset ((Bool*)datap, True, nrval); break; case TpUChar: objset ((uChar*)datap, uChar(0), nrval); break; case TpShort: objset ((Short*)datap, Short(0), nrval); break; case TpUShort: objset ((uShort*)datap, uShort(0), nrval); break; case TpInt: objset ((Int*)datap, Int(0), nrval); break; case TpUInt: objset ((uInt*)datap, uInt(0), nrval); break; case TpFloat: objset ((Float*)datap, Float(0), nrval); break; case TpDouble: objset ((Double*)datap, Double(0), nrval); break; default: break; } } } void* MSMColumn::getArrayPtr (uInt rownr) { uInt extnr = findExt(rownr, False); return ((void**)(data_p[extnr])) [rownr-ncum_p[extnr-1]]; } void MSMColumn::putArrayPtr (uInt rownr, void* ptr) { uInt extnr = findExt(rownr, False); ((void**)(data_p[extnr])) [rownr-ncum_p[extnr-1]] = ptr; } } //# NAMESPACE CASACORE - END casacore-2.4.1/tables/DataMan/MSMColumn.h000066400000000000000000000270071321422335000200410ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef TABLES_MSMCOLUMN_H #define TABLES_MSMCOLUMN_H //# Includes #include #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 StManColumn { 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(); // 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). // void getBoolV (uInt rownr, Bool* dataPtr); void getuCharV (uInt rownr, uChar* dataPtr); void getShortV (uInt rownr, Short* dataPtr); void getuShortV (uInt rownr, uShort* dataPtr); void getIntV (uInt rownr, Int* dataPtr); void getuIntV (uInt rownr, uInt* dataPtr); void getfloatV (uInt rownr, float* dataPtr); void getdoubleV (uInt rownr, double* dataPtr); void getComplexV (uInt rownr, Complex* dataPtr); void getDComplexV (uInt rownr, DComplex* dataPtr); void getStringV (uInt 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). // void putBoolV (uInt rownr, const Bool* dataPtr); void putuCharV (uInt rownr, const uChar* dataPtr); void putShortV (uInt rownr, const Short* dataPtr); void putuShortV (uInt rownr, const uShort* dataPtr); void putIntV (uInt rownr, const Int* dataPtr); void putuIntV (uInt rownr, const uInt* dataPtr); void putfloatV (uInt rownr, const float* dataPtr); void putdoubleV (uInt rownr, const double* dataPtr); void putComplexV (uInt rownr, const Complex* dataPtr); void putDComplexV (uInt rownr, const DComplex* dataPtr); void putStringV (uInt rownr, const String* dataPtr); // // Get scalars from the given row on with a maximum of nrmax values. // This can be used to get an entire column of scalars or to get // a part of a column (for a cache for example). // The buffer pointed to by dataPtr has to have the correct length // (which is guaranteed by the ScalarColumn get function). // uInt getBlockBoolV (uInt rownr, uInt nrmax, Bool* dataPtr); uInt getBlockuCharV (uInt rownr, uInt nrmax, uChar* dataPtr); uInt getBlockShortV (uInt rownr, uInt nrmax, Short* dataPtr); uInt getBlockuShortV (uInt rownr, uInt nrmax, uShort* dataPtr); uInt getBlockIntV (uInt rownr, uInt nrmax, Int* dataPtr); uInt getBlockuIntV (uInt rownr, uInt nrmax, uInt* dataPtr); uInt getBlockfloatV (uInt rownr, uInt nrmax, float* dataPtr); uInt getBlockdoubleV (uInt rownr, uInt nrmax, double* dataPtr); uInt getBlockComplexV (uInt rownr, uInt nrmax, Complex* dataPtr); uInt getBlockDComplexV (uInt rownr, uInt nrmax, DComplex* dataPtr); uInt getBlockStringV (uInt rownr, uInt nrmax, String* dataPtr); // // Put nrmax scalars from the given row on. // This can be used to put an entire column of scalars or to put // a part of a column (for a cache for example). // The buffer pointed to by dataPtr has to have the correct length // (which is guaranteed by the ScalarColumn put function). // void putBlockBoolV (uInt rownr, uInt nrmax, const Bool* dataPtr); void putBlockuCharV (uInt rownr, uInt nrmax, const uChar* dataPtr); void putBlockShortV (uInt rownr, uInt nrmax, const Short* dataPtr); void putBlockuShortV (uInt rownr, uInt nrmax, const uShort* dataPtr); void putBlockIntV (uInt rownr, uInt nrmax, const Int* dataPtr); void putBlockuIntV (uInt rownr, uInt nrmax, const uInt* dataPtr); void putBlockfloatV (uInt rownr, uInt nrmax, const float* dataPtr); void putBlockdoubleV (uInt rownr, uInt nrmax, const double* dataPtr); void putBlockComplexV (uInt rownr, uInt nrmax, const Complex* dataPtr); void putBlockDComplexV (uInt rownr, uInt nrmax, const DComplex* dataPtr); void putBlockStringV (uInt rownr, uInt nrmax, const String* 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 implementation loops through all rows. // 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 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); // // Add (newNrrow-oldNrrow) rows to the column. virtual void addRow (uInt newNrrow, uInt oldNrrow); // Resize the data blocks. // This adds an extension when needed. void resize (uInt nrval); // Remove the given row. // If no rows remain in the extension, the extension is also removed. virtual void remove (uInt rownr); // Create the number of rows in a new table. // This is used when a table gets created or opened. virtual void doCreate (uInt nrrow); // Check if the class invariants still hold. virtual Bool ok() const; protected: MSMBase* stmanPtr_p; // The data type (for caching purposes). int dtype_p; // The data is indirectly accessed via a pointer (for the derived classes). Bool byPtr_p; // The number of allocated rows in the column. uInt 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 (uInt rownr, Bool setCache); // Get the next extension. // For the first iteration extnr should be zero. // It returns the number of values in it until the maximum is reached. // Zero means no more extensions. uInt nextExt (void*& ext, uInt& extnr, uInt nrmax) const; // Allocate an extension with the data type of the column. void* allocData (uInt 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, uInt inx, uInt nrvalAfter); // Initialize the data (after an open). void initData (void* datap, uInt nrval); // Get the pointer for the given row. // This is for the derived classes like StManArrayColumnMemory. void* getArrayPtr (uInt rownr); // Put the pointer for the given row. // This is for the derived classes like StManArrayColumnMemory. void putArrayPtr (uInt rownr, void* dataPtr); private: // Forbid copy constructor. MSMColumn (const MSMColumn&); // Forbid assignment. MSMColumn& operator= (const MSMColumn&); }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/tables/DataMan/MSMDirColumn.cc000066400000000000000000000200121321422335000206230ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN MSMDirColumn::MSMDirColumn (MSMBase* smptr, int dataType) : MSMColumn (smptr, dataType, True), nrelem_p (0) {} MSMDirColumn::~MSMDirColumn() { uInt nr = stmanPtr_p->nrow(); for (uInt i=0; i* arr) { Bool deleteIt; float* data = arr->getStorage (deleteIt); objcopy (data, (const float*)(getArrayPtr (rownr)), nrelem_p); arr->putStorage (data, deleteIt); } void MSMDirColumn::putArrayfloatV (uInt rownr, const Array* arr) { Bool deleteIt; const float* data = arr->getStorage (deleteIt); objcopy ((float*)(getArrayPtr (rownr)), data, nrelem_p); arr->freeStorage (data, deleteIt); } void MSMDirColumn::getSlicefloatV (uInt rownr, const Slicer& ns, Array* arr) { Array tabarr (shape_p, (float*) (getArrayPtr (rownr)), SHARE); IPosition blc, trc, inc; ns.inferShapeFromSource (shape_p, blc, trc, inc); *arr = tabarr(blc, trc, inc); } void MSMDirColumn::putSlicefloatV (uInt rownr, const Slicer& ns, const Array* arr) { Array tabarr (shape_p, (float*) (getArrayPtr (rownr)), SHARE); IPosition blc, trc, inc; ns.inferShapeFromSource (shape_p, blc, trc, inc); tabarr(blc, trc, inc) = *arr; } #define MSMARRCOLUMN_GETPUT(T,NM) \ void MSMDirColumn::aips_name2(getArray,NM) (uInt rownr, Array* arr) \ { \ Bool deleteIt; \ T* data = arr->getStorage (deleteIt); \ objcopy (data, (const T*)(getArrayPtr (rownr)), nrelem_p); \ arr->putStorage (data, deleteIt); \ } \ void MSMDirColumn::aips_name2(putArray,NM) (uInt rownr, const Array* arr) \ { \ Bool deleteIt; \ const T* data = arr->getStorage (deleteIt); \ objcopy ((T*)(getArrayPtr (rownr)), data, nrelem_p); \ arr->freeStorage (data, deleteIt); \ } \ void MSMDirColumn::aips_name2(getSlice,NM) \ (uInt rownr, const Slicer& ns, Array* arr) \ { \ Array tabarr (shape_p, (T*) (getArrayPtr (rownr)), SHARE); \ IPosition blc, trc, inc; \ ns.inferShapeFromSource (shape_p, blc, trc, inc); \ *arr = tabarr(blc, trc, inc); \ } \ void MSMDirColumn::aips_name2(putSlice,NM) \ (uInt rownr, const Slicer& ns, const Array* arr) \ { \ Array tabarr (shape_p, (T*) (getArrayPtr (rownr)), SHARE); \ IPosition blc, trc, inc; \ ns.inferShapeFromSource (shape_p, blc, trc, inc); \ tabarr(blc, trc, inc) = *arr; \ } MSMARRCOLUMN_GETPUT(Bool,BoolV) MSMARRCOLUMN_GETPUT(uChar,uCharV) MSMARRCOLUMN_GETPUT(Short,ShortV) MSMARRCOLUMN_GETPUT(uShort,uShortV) MSMARRCOLUMN_GETPUT(Int,IntV) MSMARRCOLUMN_GETPUT(uInt,uIntV) //#//MSMARRCOLUMN_GETPUT(float,floatV) MSMARRCOLUMN_GETPUT(double,doubleV) MSMARRCOLUMN_GETPUT(Complex,ComplexV) MSMARRCOLUMN_GETPUT(DComplex,DComplexV) MSMARRCOLUMN_GETPUT(String,StringV) void MSMDirColumn::getArrayColumnfloatV (Array* arr) { uInt nrmax = arr->shape()(arr->ndim()-1); Bool deleteItTarget; float* target = arr->getStorage (deleteItTarget); uInt nr; void* ext; uInt extnr = 0; while ((nr = nextExt (ext, extnr, nrmax)) > 0) { const float** dpa = (const float**)ext; for (uInt i=0; iputStorage (target, deleteItTarget); } void MSMDirColumn::putArrayColumnfloatV (const Array* arr) { uInt nrmax = arr->shape()(arr->ndim()-1); Bool deleteItTarget; const float* target = arr->getStorage (deleteItTarget); uInt nr; void* ext; uInt extnr = 0; while ((nr = nextExt (ext, extnr, nrmax)) > 0) { float** dpa = (float**)ext; for (uInt i=0; ifreeStorage (target, deleteItTarget); } #define MSMARRCOLUMN_GETPUTCOLUMN(T,NM) \ void MSMDirColumn::aips_name2(getArrayColumn,NM) (Array* arr) \ { \ uInt nrmax = arr->shape()(arr->ndim()-1); \ Bool deleteItTarget; \ T* target = arr->getStorage (deleteItTarget); \ uInt nr; \ void* ext; \ uInt extnr = 0; \ while ((nr = nextExt (ext, extnr, nrmax)) > 0) { \ const T** dpa = (const T**)ext; \ for (uInt i=0; iputStorage (target, deleteItTarget); \ } \ void MSMDirColumn::aips_name2(putArrayColumn,NM) (const Array* arr) \ { \ uInt nrmax = arr->shape()(arr->ndim()-1); \ Bool deleteItTarget; \ const T* target = arr->getStorage (deleteItTarget); \ uInt nr; \ void* ext; \ uInt extnr = 0; \ while ((nr = nextExt (ext, extnr, nrmax)) > 0) { \ T** dpa = (T**)ext; \ for (uInt i=0; ifreeStorage (target, deleteItTarget); \ } MSMARRCOLUMN_GETPUTCOLUMN(Bool,BoolV) MSMARRCOLUMN_GETPUTCOLUMN(uChar,uCharV) MSMARRCOLUMN_GETPUTCOLUMN(Short,ShortV) MSMARRCOLUMN_GETPUTCOLUMN(uShort,uShortV) MSMARRCOLUMN_GETPUTCOLUMN(Int,IntV) MSMARRCOLUMN_GETPUTCOLUMN(uInt,uIntV) //#//MSMARRCOLUMN_GETPUTCOLUMN(float,floatV) MSMARRCOLUMN_GETPUTCOLUMN(double,doubleV) MSMARRCOLUMN_GETPUTCOLUMN(Complex,ComplexV) MSMARRCOLUMN_GETPUTCOLUMN(DComplex,DComplexV) MSMARRCOLUMN_GETPUTCOLUMN(String,StringV) void MSMDirColumn::remove (uInt rownr) { deleteArray (rownr); MSMColumn::remove (rownr); } Bool MSMDirColumn::ok() const { return MSMColumn::ok(); } void MSMDirColumn::deleteArray (uInt rownr) { void* datap = getArrayPtr (rownr); deleteData (datap, False); } } //# NAMESPACE CASACORE - END casacore-2.4.1/tables/DataMan/MSMDirColumn.h000066400000000000000000000223351321422335000204770ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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. ~MSMDirColumn(); // It can handle access to a slice in a cell. Bool canAccessSlice (Bool& reask) const; // It can handle access to an entire column. Bool canAccessArrayColumn (Bool& reask) const; // Set the (fixed) shape of the arrays in the entire column. void setShapeColumn (const IPosition& shape); // Add (newNrrow-oldNrrow) rows to the column. // Allocate the data arrays in these rows if the shape is fixed. void addRow (uInt newNrrow, uInt oldNrrow); // Get the dimensionality of the item in the given row. // 0 is returned if there is no array. uInt ndim (uInt rownr); // Get the shape of the array in the given row. // An zero-length IPosition is returned if there is no array. IPosition shape (uInt 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). // void getArrayBoolV (uInt rownr, Array* dataPtr); void getArrayuCharV (uInt rownr, Array* dataPtr); void getArrayShortV (uInt rownr, Array* dataPtr); void getArrayuShortV (uInt rownr, Array* dataPtr); void getArrayIntV (uInt rownr, Array* dataPtr); void getArrayuIntV (uInt rownr, Array* dataPtr); void getArrayfloatV (uInt rownr, Array* dataPtr); void getArraydoubleV (uInt rownr, Array* dataPtr); void getArrayComplexV (uInt rownr, Array* dataPtr); void getArrayDComplexV (uInt rownr, Array* dataPtr); void getArrayStringV (uInt rownr, Array* 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). // void putArrayBoolV (uInt rownr, const Array* dataPtr); void putArrayuCharV (uInt rownr, const Array* dataPtr); void putArrayShortV (uInt rownr, const Array* dataPtr); void putArrayuShortV (uInt rownr, const Array* dataPtr); void putArrayIntV (uInt rownr, const Array* dataPtr); void putArrayuIntV (uInt rownr, const Array* dataPtr); void putArrayfloatV (uInt rownr, const Array* dataPtr); void putArraydoubleV (uInt rownr, const Array* dataPtr); void putArrayComplexV (uInt rownr, const Array* dataPtr); void putArrayDComplexV (uInt rownr, const Array* dataPtr); void putArrayStringV (uInt rownr, const Array* 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). // void getSliceBoolV (uInt rownr, const Slicer&, Array* dataPtr); void getSliceuCharV (uInt rownr, const Slicer&, Array* dataPtr); void getSliceShortV (uInt rownr, const Slicer&, Array* dataPtr); void getSliceuShortV (uInt rownr, const Slicer&, Array* dataPtr); void getSliceIntV (uInt rownr, const Slicer&, Array* dataPtr); void getSliceuIntV (uInt rownr, const Slicer&, Array* dataPtr); void getSlicefloatV (uInt rownr, const Slicer&, Array* dataPtr); void getSlicedoubleV (uInt rownr, const Slicer&, Array* dataPtr); void getSliceComplexV (uInt rownr, const Slicer&, Array* dataPtr); void getSliceDComplexV (uInt rownr, const Slicer&, Array* dataPtr); void getSliceStringV (uInt rownr, const Slicer&, Array* 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). // void putSliceBoolV (uInt rownr, const Slicer&, const Array* dataPtr); void putSliceuCharV (uInt rownr, const Slicer&, const Array* dataPtr); void putSliceShortV (uInt rownr, const Slicer&, const Array* dataPtr); void putSliceuShortV (uInt rownr, const Slicer&, const Array* dataPtr); void putSliceIntV (uInt rownr, const Slicer&, const Array* dataPtr); void putSliceuIntV (uInt rownr, const Slicer&, const Array* dataPtr); void putSlicefloatV (uInt rownr, const Slicer&, const Array* dataPtr); void putSlicedoubleV (uInt rownr, const Slicer&, const Array* dataPtr); void putSliceComplexV (uInt rownr, const Slicer&, const Array* dataPtr); void putSliceDComplexV (uInt rownr, const Slicer&, const Array* dataPtr); void putSliceStringV (uInt rownr, const Slicer&, const Array* dataPtr); // // Get all array values in the column. // The buffer pointed to by dataPtr has to have the correct length // (which is guaranteed by the ArrayColumn getColumn function). // void getArrayColumnBoolV (Array* dataPtr); void getArrayColumnuCharV (Array* dataPtr); void getArrayColumnShortV (Array* dataPtr); void getArrayColumnuShortV (Array* dataPtr); void getArrayColumnIntV (Array* dataPtr); void getArrayColumnuIntV (Array* dataPtr); void getArrayColumnfloatV (Array* dataPtr); void getArrayColumndoubleV (Array* dataPtr); void getArrayColumnComplexV (Array* dataPtr); void getArrayColumnDComplexV (Array* dataPtr); void getArrayColumnStringV (Array* dataPtr); // // Put all arrays in the column. // The buffer pointed to by dataPtr has to have the correct length // (which is guaranteed by the ArrayColumn putColumn function). // void putArrayColumnBoolV (const Array* dataPtr); void putArrayColumnuCharV (const Array* dataPtr); void putArrayColumnShortV (const Array* dataPtr); void putArrayColumnuShortV (const Array* dataPtr); void putArrayColumnIntV (const Array* dataPtr); void putArrayColumnuIntV (const Array* dataPtr); void putArrayColumnfloatV (const Array* dataPtr); void putArrayColumndoubleV (const Array* dataPtr); void putArrayColumnComplexV (const Array* dataPtr); void putArrayColumnDComplexV (const Array* dataPtr); void putArrayColumnStringV (const Array* dataPtr); // // Remove the value in the given row. void remove (uInt rownr); // Let the column create its arrays. void doCreate (uInt nrrow); // Check if the class invariants still hold. Bool ok() const; private: // The (unique) sequence number of the column. uInt seqnr_p; // The shape of the array. IPosition shape_p; // The nr of elements in the array. uInt nrelem_p; // Delete the array in the given row. void deleteArray (uInt rownr); // Forbid copy constructor. MSMDirColumn (const MSMDirColumn&); // Forbid assignment. MSMDirColumn& operator= (const MSMDirColumn&); }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/tables/DataMan/MSMIndColumn.cc000066400000000000000000000214471321422335000206340ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include #include #include #include #include #include #include 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() { uInt nr = stmanPtr_p->nrow(); for (uInt i=0; ishape().isEqual (shape)) { return; } ptr->clear(dataType()); delete ptr; } // Create the shape and array together. // Fiddle a bit to store the shape at the beginning. ptr = new Data (shape, dataType()); 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 (uInt 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 (uInt rownr) { return (getArrayPtr(rownr) == 0 ? False : True); } uInt MSMIndColumn::ndim (uInt rownr) { return getShape(rownr)->shape().nelements(); } IPosition MSMIndColumn::shape (uInt rownr) { return getShape(rownr)->shape(); } Bool MSMIndColumn::canChangeShape() const { return True; } Bool MSMIndColumn::canAccessSlice (Bool& reask) const { reask = False; return True; } void MSMIndColumn::getArrayfloatV (uInt rownr, Array* arr) { float* ptr = static_cast(MSMINDCOLUMN_GETDATA(rownr)->data()); Bool deleteIt; float* data = arr->getStorage (deleteIt); objcopy (data, ptr, arr->nelements()); arr->putStorage (data, deleteIt); } void MSMIndColumn::putArrayfloatV (uInt rownr, const Array* arr) { float* ptr = static_cast(MSMINDCOLUMN_GETDATA(rownr)->data()); Bool deleteIt; const float* data = arr->getStorage (deleteIt); objcopy (ptr, data, arr->nelements()); arr->freeStorage (data, deleteIt); } void MSMIndColumn::getSlicefloatV (uInt rownr, const Slicer& ns, Array* arr) { Data* ptr = MSMINDCOLUMN_GETDATA(rownr); const IPosition& shape = ptr->shape(); Array tabarr (shape, static_cast(ptr->data()), SHARE); IPosition blc, trc, inc; ns.inferShapeFromSource (shape, blc, trc, inc); *arr = tabarr(blc, trc, inc); } void MSMIndColumn::putSlicefloatV (uInt rownr, const Slicer& ns, const Array* arr) { Data* ptr = MSMINDCOLUMN_GETDATA(rownr); const IPosition& shape = ptr->shape(); Array tabarr (shape, static_cast(ptr->data()), SHARE); IPosition blc, trc, inc; ns.inferShapeFromSource (shape, blc, trc, inc); tabarr(blc, trc, inc) = *arr; } #define MSMINDCOLUMN_GETPUT(T,NM) \ void MSMIndColumn::aips_name2(getArray,NM) (uInt rownr, Array* arr) \ { \ T* ptr = static_cast(MSMINDCOLUMN_GETDATA(rownr)->data()); \ Bool deleteIt; \ T* data = arr->getStorage (deleteIt); \ objcopy (data, ptr, arr->nelements()); \ arr->putStorage (data, deleteIt); \ } \ void MSMIndColumn::aips_name2(putArray,NM) (uInt rownr, const Array* arr) \ { \ T* ptr = static_cast(MSMINDCOLUMN_GETDATA(rownr)->data()); \ Bool deleteIt; \ const T* data = arr->getStorage (deleteIt); \ objcopy (ptr, data, arr->nelements()); \ arr->freeStorage (data, deleteIt); \ } \ void MSMIndColumn::aips_name2(getSlice,NM) \ (uInt rownr, const Slicer& ns, Array* arr) \ { \ Data* ptr = MSMINDCOLUMN_GETDATA(rownr); \ const IPosition& shape = ptr->shape(); \ Array tabarr (shape, static_cast(ptr->data()), SHARE); \ IPosition blc, trc, inc; \ ns.inferShapeFromSource (shape, blc, trc, inc); \ *arr = tabarr(blc, trc, inc); \ } \ void MSMIndColumn::aips_name2(putSlice,NM) \ (uInt rownr, const Slicer& ns, const Array* arr) \ { \ Data* ptr = MSMINDCOLUMN_GETDATA(rownr); \ const IPosition& shape = ptr->shape(); \ Array tabarr (shape, static_cast(ptr->data()), SHARE); \ IPosition blc, trc, inc; \ ns.inferShapeFromSource (shape, blc, trc, inc); \ tabarr(blc, trc, inc) = *arr; \ } MSMINDCOLUMN_GETPUT(Bool,BoolV) MSMINDCOLUMN_GETPUT(uChar,uCharV) MSMINDCOLUMN_GETPUT(Short,ShortV) MSMINDCOLUMN_GETPUT(uShort,uShortV) MSMINDCOLUMN_GETPUT(Int,IntV) MSMINDCOLUMN_GETPUT(uInt,uIntV) //#//MSMINDCOLUMN_GETPUT(float,floatV) MSMINDCOLUMN_GETPUT(double,doubleV) MSMINDCOLUMN_GETPUT(Complex,ComplexV) MSMINDCOLUMN_GETPUT(DComplex,DComplexV) MSMINDCOLUMN_GETPUT(String,StringV) void MSMIndColumn::remove (uInt rownr) { deleteArray (rownr); MSMColumn::remove (rownr); } void MSMIndColumn::deleteArray (uInt rownr) { Data* ptr = MSMINDCOLUMN_GETDATA(rownr); // Make sure possible IPosition storage is removed. if (ptr != 0) { ptr->clear(dataType()); delete ptr; } } MSMIndColumn::Data::Data (const IPosition& shape, int dtype) : shape_p (shape), data_p (0) { Int nelem = shape.product(); switch (dtype) { case TpBool: data_p = new Bool[nelem]; break; case TpChar: data_p = new Char[nelem]; break; case TpUChar: data_p = new uChar[nelem]; break; case TpShort: data_p = new Short[nelem]; break; case TpUShort: data_p = new uShort[nelem]; break; case TpInt: data_p = new Int[nelem]; break; case TpUInt: data_p = new uInt[nelem]; break; case TpFloat: data_p = new Float[nelem]; break; case TpDouble: data_p = new Double[nelem]; break; case TpComplex: data_p = new Complex[nelem]; break; case TpDComplex: data_p = new DComplex[nelem]; break; case TpString: data_p = new String[nelem]; break; default: throw DataManInvDT("MSMIndColumn"); } } MSMIndColumn::Data::~Data() { if (data_p != 0) { throw DataManInternalError("MSMIndColumn::dtor: data array not deleted"); } } void MSMIndColumn::Data::clear (int dtype) { switch (dtype) { case TpBool: delete [] static_cast(data_p); break; case TpChar: delete [] static_cast(data_p); break; case TpUChar: delete [] static_cast(data_p); break; case TpShort: delete [] static_cast(data_p); break; case TpUShort: delete [] static_cast(data_p); break; case TpInt: delete [] static_cast(data_p); break; case TpUInt: delete [] static_cast(data_p); break; case TpFloat: delete [] static_cast(data_p); break; case TpDouble: delete [] static_cast(data_p); break; case TpComplex: delete [] static_cast(data_p); break; case TpDComplex: delete [] static_cast(data_p); break; case TpString: delete [] static_cast(data_p); break; default: throw DataManInvDT("MSMIndColumn"); } data_p = 0; } } //# NAMESPACE CASACORE - END casacore-2.4.1/tables/DataMan/MSMIndColumn.h000066400000000000000000000214071321422335000204720ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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(); // It can handle access to a slice in a cell. Bool canAccessSlice (Bool& reask) const; // 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 (uInt rownr, const IPosition& shape); // Is the shape defined (i.e. is there an array) in this row? Bool isShapeDefined (uInt rownr); // Get the dimensionality of the item in the given row. // 0 is returned if there is no array. uInt ndim (uInt rownr); // Get the shape of the array in the given row. // An zero-length IPosition is returned if there is no array. IPosition shape (uInt rownr); // 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). // void getArrayBoolV (uInt rownr, Array* dataPtr); void getArrayuCharV (uInt rownr, Array* dataPtr); void getArrayShortV (uInt rownr, Array* dataPtr); void getArrayuShortV (uInt rownr, Array* dataPtr); void getArrayIntV (uInt rownr, Array* dataPtr); void getArrayuIntV (uInt rownr, Array* dataPtr); void getArrayfloatV (uInt rownr, Array* dataPtr); void getArraydoubleV (uInt rownr, Array* dataPtr); void getArrayComplexV (uInt rownr, Array* dataPtr); void getArrayDComplexV (uInt rownr, Array* dataPtr); void getArrayStringV (uInt rownr, Array* 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). // void putArrayBoolV (uInt rownr, const Array* dataPtr); void putArrayuCharV (uInt rownr, const Array* dataPtr); void putArrayShortV (uInt rownr, const Array* dataPtr); void putArrayuShortV (uInt rownr, const Array* dataPtr); void putArrayIntV (uInt rownr, const Array* dataPtr); void putArrayuIntV (uInt rownr, const Array* dataPtr); void putArrayfloatV (uInt rownr, const Array* dataPtr); void putArraydoubleV (uInt rownr, const Array* dataPtr); void putArrayComplexV (uInt rownr, const Array* dataPtr); void putArrayDComplexV (uInt rownr, const Array* dataPtr); void putArrayStringV (uInt rownr, const Array* 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). // void getSliceBoolV (uInt rownr, const Slicer&, Array* dataPtr); void getSliceuCharV (uInt rownr, const Slicer&, Array* dataPtr); void getSliceShortV (uInt rownr, const Slicer&, Array* dataPtr); void getSliceuShortV (uInt rownr, const Slicer&, Array* dataPtr); void getSliceIntV (uInt rownr, const Slicer&, Array* dataPtr); void getSliceuIntV (uInt rownr, const Slicer&, Array* dataPtr); void getSlicefloatV (uInt rownr, const Slicer&, Array* dataPtr); void getSlicedoubleV (uInt rownr, const Slicer&, Array* dataPtr); void getSliceComplexV (uInt rownr, const Slicer&, Array* dataPtr); void getSliceDComplexV (uInt rownr, const Slicer&, Array* dataPtr); void getSliceStringV (uInt rownr, const Slicer&, Array* 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). // void putSliceBoolV (uInt rownr, const Slicer&, const Array* dataPtr); void putSliceuCharV (uInt rownr, const Slicer&, const Array* dataPtr); void putSliceShortV (uInt rownr, const Slicer&, const Array* dataPtr); void putSliceuShortV (uInt rownr, const Slicer&, const Array* dataPtr); void putSliceIntV (uInt rownr, const Slicer&, const Array* dataPtr); void putSliceuIntV (uInt rownr, const Slicer&, const Array* dataPtr); void putSlicefloatV (uInt rownr, const Slicer&, const Array* dataPtr); void putSlicedoubleV (uInt rownr, const Slicer&, const Array* dataPtr); void putSliceComplexV (uInt rownr, const Slicer&, const Array* dataPtr); void putSliceDComplexV (uInt rownr, const Slicer&, const Array* dataPtr); void putSliceStringV (uInt rownr, const Slicer&, const Array* dataPtr); // // Remove the value in the given row. // This will result in lost file space. void remove (uInt rownr); private: class Data { public: Data (const IPosition& shape, int dtype); ~Data(); void clear (int dtype); const IPosition& shape() const {return shape_p;} void* data() {return data_p;} private: Data (const Data&); Data& operator= (const Data&); IPosition shape_p; void* data_p; }; // The shape of all arrays in case it is fixed. IPosition fixedShape_p; // The size of an array element. uInt elemSize_p; // The size at the start of the data (for the IPosition). uInt startSize_p; // Delete the array in the given row. void deleteArray (uInt rownr); // Read the shape at the given row. // It throws an exception if undefined. Data* getShape (uInt rownr); // Get a pointer to the data array. void* getDataPtr (uInt rownr) { return (char*)(getShape(rownr)) + startSize_p; } // Forbid copy constructor. MSMIndColumn (const MSMIndColumn&); // Forbid assignment. MSMIndColumn& operator= (const MSMIndColumn&); }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/tables/DataMan/MappedArrayEngine.h000066400000000000000000000175411321422335000215640ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (uInt 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(); // 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&); // Assignment is not needed and therefore forbidden // (so it is made private and not implemented). MappedArrayEngine& operator= (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-2.4.1/tables/DataMan/MappedArrayEngine.tcc000066400000000000000000000100231321422335000220720ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/tables/DataMan/MemoryStMan.cc000066400000000000000000000030711321422335000205730ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN MemoryStMan::MemoryStMan() : MSMBase() {} MemoryStMan::MemoryStMan (const String& storageManagerName) : MSMBase (storageManagerName) {} MemoryStMan::~MemoryStMan() {} } //# NAMESPACE CASACORE - END casacore-2.4.1/tables/DataMan/MemoryStMan.h000066400000000000000000000064631321422335000204450ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/tables/DataMan/RetypedArrayEngine.h000066400000000000000000000660551321422335000217760ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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); // uInt 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(); // 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&); // Assignment is not needed and therefore forbidden // (so it is made private and not implemented). RetypedArrayEngine& operator= (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 create (uInt 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 (uInt rownr, const IPosition& shape); // Get the dimensionality of the array in the given row. uInt ndim (uInt 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 (uInt 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 (uInt 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-2.4.1/tables/DataMan/RetypedArrayEngine.tcc000066400000000000000000000250601321422335000223070ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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.resize (shp.nelements()); shape_p = IPosition(shp); } 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 vector; thisCol.keywordSet().get ("_RetypedArrayEngine_Shape", vector); shape_p.resize (vector.nelements()); shape_p = IPosition (vector); 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::create (uInt initialNrrow) { BaseMappedArrayEngine::create (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 (uInt 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 (uInt rownr) { return column().ndim (rownr) - shape_p.nelements(); } template IPosition RetypedArrayEngine::shape (uInt 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 (uInt 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")); } uInt i; //# 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()); 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-2.4.1/tables/DataMan/RetypedArraySetGet.h000066400000000000000000000131531321422335000217530ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef TABLES_RETYPEDARRAYSETGET_H #define TABLES_RETYPEDARRAYSETGET_H //# Includes #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations template class Array; 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-2.4.1/tables/DataMan/RetypedArraySetGet.tcc000066400000000000000000000104461321422335000222770ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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. uInt 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. uInt 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-2.4.1/tables/DataMan/SSMBase.cc000066400000000000000000001014171321422335000176200ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include #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 (max(aCacheSize,2u)), 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 (max(aCacheSize,2u)), 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 bucketrows 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 ("ActualCacheSize", Int(itsCacheSize)); return rec; } void SSMBase::setProperties (const Record& rec) { if (rec.isDefined("ActualCacheSize")) { setCacheSize (rec.asInt("ActualCacheSize"), 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. CountedPtr aFio = itsFile->makeFilebufIO (512); // It is stored in big or little endian canonical format. TypeIO* aTio; if (asBigEndian()) { aTio = new CanonicalIO (aFio.get()); } else { aTio = new LECanonicalIO (aFio.get()); } 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(); delete aTio; for (uInt i=0; i 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(); delete aMio; } void SSMBase::writeIndex() { TypeIO* aTio; TypeIO* aMio; MemoryIO aMemBuf; // 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. CountedPtr aFio = itsFile->makeFilebufIO (512); uInt aCLength = 2*CanonicalConversion::canonicalSize(&itsFirstIdxBucket); // Store it in big or little endian canonical format. if (asBigEndian()) { aMio = new CanonicalIO (&aMemBuf); aTio = new CanonicalIO (aFio.get()); } else { aMio = new LECanonicalIO (&aMemBuf); aTio = new LECanonicalIO (aFio.get()); } 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 writying the index the next time. // Set the index offset variable accordingly. if (idxLength <= idxBucketSize/2) { itsIdxBucketOffset = aCLength; } else { itsIdxBucketOffset = 0; } } itsNrIdxBuckets = aNrBuckets; delete aMio; 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(); delete aTio; 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::addRow (uInt 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::removeRow (uInt 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; create(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(uInt aRowNr, uInt aColNr, uInt& aStartRow, uInt& 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 != 0) { itsCache->flush(); } if (isDataChanged) { writeIndex(); if (doFsync) { itsFile->fsync(); } changed = True; isDataChanged = False; } if (itsIosFile != 0) { itsIosFile->flush(doFsync); } ios.putstart ("SSM", 2); ios << itsDataManName; putBlock (ios, itsColumnOffset, itsColumnOffset.nelements()); putBlock (ios, itsColIndexMap, itsColIndexMap.nelements()); ios.putend(); return changed; } void SSMBase::resync (uInt 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); } } void SSMBase::create (uInt aNrRows) { init(); recreate(); itsNrRows = 0; addRow (aNrRows); } void SSMBase::open (uInt 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); } } 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-2.4.1/tables/DataMan/SSMBase.h000066400000000000000000000411051321422335000174570ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 ActualCacheSize 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. uInt 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 (uInt aRowNr, uInt aColNr, uInt& aStartRow, uInt& 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 create (uInt aNrRows); // Open the storage manager file for an existing table, read in // the data, and let the SSMColumn objects read their data. virtual void open (uInt aRowNr, AipsIO&); // Resync the storage manager with the new file contents. // This is done by clearing the cache. virtual void resync (uInt 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 addRow (uInt aNrRows); // Delete a row from all columns. virtual void removeRow (uInt 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. uInt 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 uInt 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-2.4.1/tables/DataMan/SSMColumn.cc000066400000000000000000000537771321422335000202220ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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) : StManColumn (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 (uInt) { return itsShape.nelements(); } IPosition SSMColumn::shape (uInt) { return itsShape; } void SSMColumn::doCreate(uInt) { } void SSMColumn::getFile(uInt) { } void SSMColumn::addRow (uInt aNewNrRows, uInt, Bool doInit) { if (doInit && dataType() == TpString) { uInt aRowNr=0; uInt aNrRows=aNewNrRows; while (aNrRows > 0) { uInt aStartRow; uInt anEndRow; char* aValPtr; aValPtr = itsSSMPtr->find (aRowNr, itsColNr, aStartRow, anEndRow, columnName()); aRowNr = anEndRow+1; uInt aNr = anEndRow-aStartRow+1; aNrRows -= aNr; memset(aValPtr, 0, aNr * itsExternalSizeBytes); itsSSMPtr->setBucketDirty(); } } } void SSMColumn::deleteRow(uInt aRowNr) { char* aValue; uInt aSRow; uInt 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; getBoolV(aRowNr,&aVal); } // first check if aRowNr is in cache, if not, fill cache // In both cases remove row from cache uInt aStartRow = columnCache().start(); uInt 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 uInt 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, uInt aRowNr, uInt aSRow, uInt anERow) { // Shift from aRrowNr on 1 to the left. char* aToPtr = aValue + (aRowNr-aSRow) * itsExternalSizeBytes; char* aFromPtr = aToPtr + itsExternalSizeBytes; uInt 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::getBoolV (uInt aRowNr, Bool* aValue) { getValue(aRowNr); *aValue = static_cast(itsData)[aRowNr-columnCache().start()]; } void SSMColumn::getuCharV (uInt aRowNr, uChar* aValue) { getValue(aRowNr); *aValue = static_cast(itsData)[aRowNr-columnCache().start()]; } void SSMColumn::getShortV (uInt aRowNr, Short* aValue) { getValue(aRowNr); *aValue = static_cast(itsData)[aRowNr-columnCache().start()]; } void SSMColumn::getuShortV (uInt aRowNr, uShort* aValue) { getValue(aRowNr); *aValue = static_cast(itsData)[aRowNr-columnCache().start()]; } void SSMColumn::getIntV (uInt aRowNr, Int* aValue) { getValue(aRowNr); *aValue = static_cast(itsData)[aRowNr-columnCache().start()]; } void SSMColumn::getuIntV (uInt aRowNr, uInt* aValue) { getValue(aRowNr); *aValue = static_cast(itsData)[aRowNr-columnCache().start()]; } void SSMColumn::getfloatV (uInt aRowNr, float* aValue) { getValue(aRowNr); *aValue = static_cast(itsData)[aRowNr-columnCache().start()]; } void SSMColumn::getdoubleV (uInt aRowNr, double* aValue) { getValue(aRowNr); *aValue = static_cast(itsData)[aRowNr-columnCache().start()]; } void SSMColumn::getComplexV (uInt aRowNr, Complex* aValue) { getValue(aRowNr); *aValue = static_cast(itsData)[aRowNr-columnCache().start()]; } void SSMColumn::getDComplexV (uInt aRowNr,DComplex* aValue) { getValue(aRowNr); *aValue = static_cast(itsData)[aRowNr-columnCache().start()]; } void SSMColumn::getStringV (uInt 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()); uInt aStartRow; uInt 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, uInt aRowNr) { uInt aStartRow; uInt 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(uInt aRowNr) { if (aRowNr < columnCache().start() || aRowNr > columnCache().end()) { uInt aStartRow; uInt 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::putBoolV (uInt aRowNr, const Bool* aValue) { uInt aStartRow; uInt anEndRow; char* aDummy; aDummy = itsSSMPtr->find (aRowNr, itsColNr, aStartRow, anEndRow, columnName()); uInt 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::putuCharV (uInt aRowNr, const uChar* aValue) { putValue(aRowNr,aValue); if (aRowNr >= columnCache().start() && aRowNr <= columnCache().end()) { static_cast(itsData)[aRowNr-columnCache().start()] = *aValue; } } void SSMColumn::putShortV (uInt aRowNr, const Short* aValue) { putValue(aRowNr,aValue); if (aRowNr >= columnCache().start() && aRowNr <= columnCache().end()) { static_cast(itsData)[aRowNr-columnCache().start()] = *aValue; } } void SSMColumn::putuShortV (uInt aRowNr, const uShort* aValue) { putValue(aRowNr,aValue); if (aRowNr >= columnCache().start() && aRowNr <= columnCache().end()) { static_cast(itsData)[aRowNr-columnCache().start()] = *aValue; } } void SSMColumn::putIntV (uInt aRowNr, const Int* aValue) { putValue(aRowNr,aValue); if (aRowNr >= columnCache().start() && aRowNr <= columnCache().end()) { static_cast(itsData)[aRowNr-columnCache().start()] = *aValue; } } void SSMColumn::putuIntV (uInt aRowNr, const uInt* aValue) { putValue(aRowNr,aValue); if (aRowNr >= columnCache().start() && aRowNr <= columnCache().end()) { static_cast(itsData)[aRowNr-columnCache().start()] = *aValue; } } void SSMColumn::putfloatV (uInt aRowNr, const float* aValue) { putValue(aRowNr,aValue); if (aRowNr >= columnCache().start() && aRowNr <= columnCache().end()) { static_cast(itsData)[aRowNr-columnCache().start()] = *aValue; } } void SSMColumn::putdoubleV (uInt aRowNr, const double* aValue) { putValue(aRowNr,aValue); if (aRowNr >= columnCache().start() && aRowNr <= columnCache().end()) { static_cast(itsData)[aRowNr-columnCache().start()] = *aValue; } } void SSMColumn::putComplexV (uInt aRowNr, const Complex* aValue) { putValue(aRowNr,aValue); if (aRowNr >= columnCache().start() && aRowNr <= columnCache().end()) { static_cast(itsData)[aRowNr-columnCache().start()] = *aValue; } } void SSMColumn::putDComplexV (uInt aRowNr, const DComplex* aValue) { putValue(aRowNr,aValue); if (aRowNr >= columnCache().start() && aRowNr <= columnCache().end()) { static_cast(itsData)[aRowNr-columnCache().start()] = *aValue; } } void SSMColumn::putStringV (uInt aRowNr, const String* aValue) { // Fixed length strings are written directly. if (itsMaxLen > 0) { uInt aStartRow; uInt 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(uInt aRowNr, const void* aValue) { uInt aStartRow; uInt anEndRow; char* aDummy = itsSSMPtr->find (aRowNr, itsColNr, aStartRow, anEndRow, columnName()); itsWriteFunc (aDummy+(aRowNr-aStartRow)*itsExternalSizeBytes, aValue, itsNrCopy); itsSSMPtr->setBucketDirty(); } void SSMColumn::putValueShortString(uInt aRowNr, const void* aValue, const String& string) { uInt aStartRow; uInt 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::getScalarColumnBoolV (Vector* aDataPtr) { Bool deleteIt; Bool* anArray=aDataPtr->getStorage(deleteIt); getColumnValue(anArray,aDataPtr->nelements()); aDataPtr->putStorage(anArray,deleteIt); } void SSMColumn::getScalarColumnuCharV (Vector* aDataPtr) { Bool deleteIt; uChar* anArray=aDataPtr->getStorage(deleteIt); getColumnValue(anArray,aDataPtr->nelements()); aDataPtr->putStorage(anArray,deleteIt); } void SSMColumn::getScalarColumnShortV (Vector* aDataPtr) { Bool deleteIt; Short* anArray=aDataPtr->getStorage(deleteIt); getColumnValue(anArray,aDataPtr->nelements()); aDataPtr->putStorage(anArray,deleteIt); } void SSMColumn::getScalarColumnuShortV (Vector* aDataPtr) { Bool deleteIt; uShort* anArray=aDataPtr->getStorage(deleteIt); getColumnValue(anArray,aDataPtr->nelements()); aDataPtr->putStorage(anArray,deleteIt); } void SSMColumn::getScalarColumnIntV (Vector* aDataPtr) { Bool deleteIt; Int* anArray=aDataPtr->getStorage(deleteIt); getColumnValue(anArray,aDataPtr->nelements()); aDataPtr->putStorage(anArray,deleteIt); } void SSMColumn::getScalarColumnuIntV (Vector* aDataPtr) { Bool deleteIt; uInt* anArray=aDataPtr->getStorage(deleteIt); getColumnValue(anArray,aDataPtr->nelements()); aDataPtr->putStorage(anArray,deleteIt); } void SSMColumn::getScalarColumnfloatV (Vector* aDataPtr) { Bool deleteIt; float* anArray=aDataPtr->getStorage(deleteIt); getColumnValue(anArray,aDataPtr->nelements()); aDataPtr->putStorage(anArray,deleteIt); } void SSMColumn::getScalarColumndoubleV (Vector* aDataPtr) { Bool deleteIt; double* anArray=aDataPtr->getStorage(deleteIt); getColumnValue(anArray,aDataPtr->nelements()); aDataPtr->putStorage(anArray,deleteIt); } void SSMColumn::getScalarColumnComplexV (Vector* aDataPtr) { Bool deleteIt; Complex* anArray=aDataPtr->getStorage(deleteIt); getColumnValue(anArray,aDataPtr->nelements()); aDataPtr->putStorage(anArray,deleteIt); } void SSMColumn::getScalarColumnDComplexV (Vector* aDataPtr) { Bool deleteIt; DComplex* anArray=aDataPtr->getStorage(deleteIt); getColumnValue(anArray,aDataPtr->nelements()); aDataPtr->putStorage(anArray,deleteIt); } void SSMColumn::getScalarColumnStringV (Vector* aDataPtr) { for (uInt i=0;inelements(); i++) { getStringV(i,&(*aDataPtr)(i)); } } void SSMColumn::getColumnValue(void* anArray,uInt aNrRows) { char* aDataPtr = static_cast(anArray); uInt aRowNr=0; Int rowsToDo = aNrRows; while (rowsToDo > 0) { uInt aStartRow; uInt anEndRow; char* aValue; aValue = itsSSMPtr->find (aRowNr, itsColNr, aStartRow, anEndRow, columnName()); aRowNr = anEndRow+1; uInt aNr = anEndRow-aStartRow+1; rowsToDo -= aNr; itsReadFunc (aDataPtr, aValue, aNr * itsNrCopy); aDataPtr += aNr * itsLocalSize; } } void SSMColumn::putScalarColumnBoolV (const Vector* aDataPtr) { Bool deleteIt; const Bool* anArray=aDataPtr->getStorage(deleteIt); putColumnValue(anArray,aDataPtr->nelements()); aDataPtr->freeStorage(anArray,deleteIt); } void SSMColumn::putScalarColumnuCharV (const Vector* aDataPtr) { Bool deleteIt; const uChar* anArray=aDataPtr->getStorage(deleteIt); putColumnValue(anArray,aDataPtr->nelements()); aDataPtr->freeStorage(anArray,deleteIt); } void SSMColumn::putScalarColumnShortV (const Vector* aDataPtr) { Bool deleteIt; const Short* anArray=aDataPtr->getStorage(deleteIt); putColumnValue(anArray,aDataPtr->nelements()); aDataPtr->freeStorage(anArray,deleteIt); } void SSMColumn::putScalarColumnuShortV (const Vector* aDataPtr) { Bool deleteIt; const uShort* anArray=aDataPtr->getStorage(deleteIt); putColumnValue(anArray,aDataPtr->nelements()); aDataPtr->freeStorage(anArray,deleteIt); } void SSMColumn::putScalarColumnIntV (const Vector* aDataPtr) { Bool deleteIt; const Int* anArray=aDataPtr->getStorage(deleteIt); putColumnValue(anArray,aDataPtr->nelements()); aDataPtr->freeStorage(anArray,deleteIt); } void SSMColumn::putScalarColumnuIntV (const Vector* aDataPtr) { Bool deleteIt; const uInt* anArray=aDataPtr->getStorage(deleteIt); putColumnValue(anArray,aDataPtr->nelements()); aDataPtr->freeStorage(anArray,deleteIt); } void SSMColumn::putScalarColumnfloatV (const Vector* aDataPtr) { Bool deleteIt; const float* anArray=aDataPtr->getStorage(deleteIt); putColumnValue(anArray,aDataPtr->nelements()); aDataPtr->freeStorage(anArray,deleteIt); } void SSMColumn::putScalarColumndoubleV (const Vector* aDataPtr) { Bool deleteIt; const double* anArray=aDataPtr->getStorage(deleteIt); putColumnValue(anArray,aDataPtr->nelements()); aDataPtr->freeStorage(anArray,deleteIt); } void SSMColumn::putScalarColumnComplexV (const Vector* aDataPtr) { Bool deleteIt; const Complex* anArray=aDataPtr->getStorage(deleteIt); putColumnValue(anArray,aDataPtr->nelements()); aDataPtr->freeStorage(anArray,deleteIt); } void SSMColumn::putScalarColumnDComplexV (const Vector* aDataPtr) { Bool deleteIt; const DComplex* anArray=aDataPtr->getStorage(deleteIt); putColumnValue(anArray,aDataPtr->nelements()); aDataPtr->freeStorage(anArray,deleteIt); } void SSMColumn::putScalarColumnStringV (const Vector* aDataPtr) { for (uInt i=0;inelements(); i++) { putStringV(i,&(*aDataPtr)(i)); } } void SSMColumn::putColumnValue(const void* anArray,uInt aNrRows) { const char* aDataPtr = static_cast(anArray); uInt aRowNr=0; Int rowsToDo=aNrRows; while (rowsToDo > 0) { uInt aStartRow; uInt anEndRow; char* aValPtr; aValPtr = itsSSMPtr->find (aRowNr, itsColNr, aStartRow, anEndRow, columnName()); aRowNr = anEndRow+1; uInt 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 (uInt 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 (uInt) { // Invalidate the last value read. columnCache().invalidate(); } } //# NAMESPACE CASACORE - END casacore-2.4.1/tables/DataMan/SSMColumn.h000066400000000000000000000303711321422335000200450ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 StManColumn { 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(); // 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 (uInt aRowNr); // Get the shape of the array in the given row. virtual IPosition shape (uInt aRowNr); // Let the object initialize itself for a newly created table. // It is meant for a derived class. virtual void doCreate (uInt aNrRows); // Let the column object initialize itself for an existing table virtual void getFile (uInt aNrRows); // Resync the storage manager with the new file contents. // It resets the last rownr put. void resync (uInt aNrRow); // Get the scalar value in the given row. // virtual void getBoolV (uInt aRowNr, Bool* aDataPtr); virtual void getuCharV (uInt aRowNr, uChar* aDataPtr); virtual void getShortV (uInt aRowNr, Short* aDataPtr); virtual void getuShortV (uInt aRowNr, uShort* aDataPtr); virtual void getIntV (uInt aRowNr, Int* aDataPtr); virtual void getuIntV (uInt aRowNr, uInt* aDataPtr); virtual void getfloatV (uInt aRowNr, float* aDataPtr); virtual void getdoubleV (uInt aRowNr, double* aDataPtr); virtual void getComplexV (uInt aRowNr, Complex* aDataPtr); virtual void getDComplexV (uInt aRowNr, DComplex* aDataPtr); virtual void getStringV (uInt 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 putBoolV (uInt aRowNr, const Bool* aDataPtr); virtual void putuCharV (uInt aRowNr, const uChar* aDataPtr); virtual void putShortV (uInt aRowNr, const Short* aDataPtr); virtual void putuShortV (uInt aRowNr, const uShort* aDataPtr); virtual void putIntV (uInt aRowNr, const Int* aDataPtr); virtual void putuIntV (uInt aRowNr, const uInt* aDataPtr); virtual void putfloatV (uInt aRowNr, const float* aDataPtr); virtual void putdoubleV (uInt aRowNr, const double* aDataPtr); virtual void putComplexV (uInt aRowNr, const Complex* aDataPtr); virtual void putDComplexV (uInt aRowNr, const DComplex* aDataPtr); virtual void putStringV (uInt aRowNr, const String* aDataPtr); // // Get the scalar values of the entire column. // virtual void getScalarColumnBoolV (Vector* aDataPtr); virtual void getScalarColumnuCharV (Vector* aDataPtr); virtual void getScalarColumnShortV (Vector* aDataPtr); virtual void getScalarColumnuShortV (Vector* aDataPtr); virtual void getScalarColumnIntV (Vector* aDataPtr); virtual void getScalarColumnuIntV (Vector* aDataPtr); virtual void getScalarColumnfloatV (Vector* aDataPtr); virtual void getScalarColumndoubleV (Vector* aDataPtr); virtual void getScalarColumnComplexV (Vector* aDataPtr); virtual void getScalarColumnDComplexV (Vector* aDataPtr); virtual void getScalarColumnStringV (Vector* aDataPtr); // // Put the scalar values of the entire column. // It invalidates the cache. // virtual void putScalarColumnBoolV (const Vector* aDataPtr); virtual void putScalarColumnuCharV (const Vector* aDataPtr); virtual void putScalarColumnShortV (const Vector* aDataPtr); virtual void putScalarColumnuShortV (const Vector* aDataPtr); virtual void putScalarColumnIntV (const Vector* aDataPtr); virtual void putScalarColumnuIntV (const Vector* aDataPtr); virtual void putScalarColumnfloatV (const Vector* aDataPtr); virtual void putScalarColumndoubleV (const Vector* aDataPtr); virtual void putScalarColumnComplexV (const Vector* aDataPtr); virtual void putScalarColumnDComplexV (const Vector* aDataPtr); virtual void putScalarColumnStringV (const Vector* aDataPtr); // // Add (NewNrRows-OldNrRows) rows to the Column and initialize // the new rows when needed. virtual void addRow (uInt aNewNrRows, uInt 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 (uInt 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, uInt rowNr, uInt startRow, uInt endRow); // Fill the cache with data of the bucket containing the given row. void getValue (uInt 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, uInt aRowNr); // Put the given value for the row into the correct data bucket. void putValue (uInt 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 (uInt 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, uInt 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, uInt 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: // Forbid copy constructor. SSMColumn (const SSMColumn&); // Forbid assignment. SSMColumn& operator= (const SSMColumn&); // 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-2.4.1/tables/DataMan/SSMDirColumn.cc000066400000000000000000000207271321422335000206460ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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(uInt aRowNr) { char* aValue; uInt aSRow; uInt 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) { uInt anOffr = (aRowNr-aSRow+1) * itsNrCopy; uInt anOfto = (aRowNr-aSRow) * itsNrCopy; uInt 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::getArrayBoolV (uInt aRowNr, Array* aDataPtr) { Bool deleteIt; uInt aStartRow; uInt anEndRow; char* aValue; Bool* data = aDataPtr->getStorage (deleteIt); aValue = itsSSMPtr->find (aRowNr, itsColNr, aStartRow, anEndRow, columnName()); uInt anOff = (aRowNr-aStartRow) * itsNrCopy; Conversion::bitToBool(data, aValue+ anOff/8, anOff%8, itsNrCopy); aDataPtr->putStorage (data, deleteIt); } void SSMDirColumn::getArrayuCharV (uInt aRowNr, Array* aDataPtr) { Bool deleteIt; uChar* data = aDataPtr->getStorage (deleteIt); getValue (aRowNr, data); aDataPtr->putStorage (data, deleteIt); } void SSMDirColumn::getArrayShortV (uInt aRowNr, Array* aDataPtr) { Bool deleteIt; Short* data = aDataPtr->getStorage (deleteIt); getValue (aRowNr, data); aDataPtr->putStorage (data, deleteIt); } void SSMDirColumn::getArrayuShortV (uInt aRowNr, Array* aDataPtr) { Bool deleteIt; uShort* data = aDataPtr->getStorage (deleteIt); getValue (aRowNr, data); aDataPtr->putStorage (data, deleteIt); } void SSMDirColumn::getArrayIntV (uInt aRowNr, Array* aDataPtr) { Bool deleteIt; Int* data = aDataPtr->getStorage (deleteIt); getValue (aRowNr, data); aDataPtr->putStorage (data, deleteIt); } void SSMDirColumn::getArrayuIntV (uInt aRowNr, Array* aDataPtr) { Bool deleteIt; uInt* data = aDataPtr->getStorage (deleteIt); getValue (aRowNr, data); aDataPtr->putStorage (data, deleteIt); } void SSMDirColumn::getArrayfloatV (uInt aRowNr, Array* aDataPtr) { Bool deleteIt; float* data = aDataPtr->getStorage (deleteIt); getValue (aRowNr, data); aDataPtr->putStorage (data, deleteIt); } void SSMDirColumn::getArraydoubleV (uInt aRowNr, Array* aDataPtr) { Bool deleteIt; double* data = aDataPtr->getStorage (deleteIt); getValue (aRowNr, data); aDataPtr->putStorage (data, deleteIt); } void SSMDirColumn::getArrayComplexV (uInt aRowNr, Array* aDataPtr) { Bool deleteIt; Complex* data = aDataPtr->getStorage (deleteIt); getValue (aRowNr, data); aDataPtr->putStorage (data, deleteIt); } void SSMDirColumn::getArrayDComplexV (uInt aRowNr, Array* aDataPtr) { Bool deleteIt; DComplex* data = aDataPtr->getStorage (deleteIt); getValue (aRowNr, data); aDataPtr->putStorage (data, deleteIt); } void SSMDirColumn::getArrayStringV (uInt aRowNr, Array* aDataPtr) { Int buf[3]; getRowValue(buf, aRowNr); itsSSMPtr->getStringHandler()->get(*aDataPtr, buf[0], buf[1], buf[2],False); } void SSMDirColumn::getValue(uInt aRowNr, void* data) { uInt aStartRow; uInt anEndRow; char* aValue; aValue = itsSSMPtr->find (aRowNr, itsColNr, aStartRow, anEndRow, columnName()); itsReadFunc (data, aValue+(aRowNr-aStartRow)*itsExternalSizeBytes, itsNrCopy); } void SSMDirColumn::putArrayBoolV (uInt aRowNr, const Array* aDataPtr) { Bool deleteIt; const Bool* data = aDataPtr->getStorage (deleteIt); uInt aStartRow; uInt anEndRow; char* aValue; aValue = itsSSMPtr->find (aRowNr, itsColNr, aStartRow, anEndRow, columnName()); uInt anOff = (aRowNr-aStartRow) * itsNrCopy; Conversion::boolToBit (aValue + anOff/8, data, anOff%8, itsNrCopy); itsSSMPtr->setBucketDirty(); aDataPtr->freeStorage (data, deleteIt); } void SSMDirColumn::putArrayuCharV (uInt aRowNr, const Array* aDataPtr) { Bool deleteIt; const uChar* data = aDataPtr->getStorage (deleteIt); putValue (aRowNr, data); aDataPtr->freeStorage (data, deleteIt); } void SSMDirColumn::putArrayShortV (uInt aRowNr, const Array* aDataPtr) { Bool deleteIt; const Short* data = aDataPtr->getStorage (deleteIt); putValue (aRowNr, data); aDataPtr->freeStorage (data, deleteIt); } void SSMDirColumn::putArrayuShortV (uInt aRowNr, const Array* aDataPtr) { Bool deleteIt; const uShort* data = aDataPtr->getStorage (deleteIt); putValue (aRowNr, data); aDataPtr->freeStorage (data, deleteIt); } void SSMDirColumn::putArrayIntV (uInt aRowNr, const Array* aDataPtr) { Bool deleteIt; const Int* data = aDataPtr->getStorage (deleteIt); putValue (aRowNr, data); aDataPtr->freeStorage (data, deleteIt); } void SSMDirColumn::putArrayuIntV (uInt aRowNr, const Array* aDataPtr) { Bool deleteIt; const uInt* data = aDataPtr->getStorage (deleteIt); putValue (aRowNr, data); aDataPtr->freeStorage (data, deleteIt); } void SSMDirColumn::putArrayfloatV (uInt aRowNr, const Array* aDataPtr) { Bool deleteIt; const float* data = aDataPtr->getStorage (deleteIt); putValue (aRowNr, data); aDataPtr->freeStorage (data, deleteIt); } void SSMDirColumn::putArraydoubleV (uInt aRowNr, const Array* aDataPtr) { Bool deleteIt; const double* data = aDataPtr->getStorage (deleteIt); putValue (aRowNr, data); aDataPtr->freeStorage (data, deleteIt); } void SSMDirColumn::putArrayComplexV (uInt aRowNr, const Array* aDataPtr) { Bool deleteIt; const Complex* data = aDataPtr->getStorage (deleteIt); putValue (aRowNr, data); aDataPtr->freeStorage (data, deleteIt); } void SSMDirColumn::putArrayDComplexV (uInt aRowNr, const Array* aDataPtr) { Bool deleteIt; const DComplex* data = aDataPtr->getStorage (deleteIt); putValue (aRowNr, data); aDataPtr->freeStorage (data, deleteIt); } void SSMDirColumn::putArrayStringV (uInt aRowNr, const Array* aDataPtr) { 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], *aDataPtr,False); putValue(aRowNr, buf); } } //# NAMESPACE CASACORE - END casacore-2.4.1/tables/DataMan/SSMDirColumn.h000066400000000000000000000123501321422335000205010ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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(); // 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 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 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 an array value in the given row. // 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 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); // // Remove the given row from the data bucket and possibly string bucket. virtual void deleteRow(uInt aRowNr); protected: // Read the array data for the given row into the data buffer. void getValue (uInt aRowNr, void* data); private: // Forbid copy constructor. SSMDirColumn (const SSMDirColumn&); // Forbid assignment. SSMDirColumn& operator= (const SSMDirColumn&); }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/tables/DataMan/SSMIndColumn.cc000066400000000000000000000177421321422335000206450ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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 (uInt aNrRows) { // Initialize and create new file. itsIosFile = itsSSMPtr->openArrayFile (ByteIO::New); addRow(aNrRows,0,False); } void SSMIndColumn::getFile (uInt) { // Initialize and open existing file. itsIosFile = itsSSMPtr->openArrayFile (itsSSMPtr->fileOption()); } void SSMIndColumn::addRow (uInt aNewNrRows, uInt anOldNrRows, Bool doInit) { // init the buckets to zero of needed if (doInit) { uInt aRowNr=0; uInt aNrRows=aNewNrRows; while (aNrRows > 0) { uInt aStartRow; uInt anEndRow; char* aValPtr; aValPtr = itsSSMPtr->find (aRowNr, itsColNr, aStartRow, anEndRow, columnName()); aRowNr = anEndRow+1; uInt 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 (; anOldNrRowsfind (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 (uInt 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 (uInt aRowNr) { return (getArrayPtr(aRowNr) == 0 ? False : True); } uInt SSMIndColumn::ndim (uInt aRowNr) { return getShape(aRowNr)->shape().nelements(); } IPosition SSMIndColumn::shape (uInt aRowNr) { return getShape(aRowNr)->shape(); } Bool SSMIndColumn::canChangeShape() const { return (isShapeFixed ? False : True); } Bool SSMIndColumn::canAccessSlice (Bool& reask) const { reask = False; return True; } void SSMIndColumn::deleteRow(uInt aRowNr) { char* aValue; uInt aSRow; uInt anERow; aValue = itsSSMPtr->find (aRowNr, itsColNr, aSRow, anERow, columnName()); if (aRowNr < anERow) { // remove from bucket shiftRows(aValue,aRowNr,aSRow,anERow); itsSSMPtr->setBucketDirty(); } } void SSMIndColumn::getArrayStringV (uInt aRowNr, Array* arr) { getShape(aRowNr)->getArrayStringV (*itsIosFile, arr); } void SSMIndColumn::putArrayStringV (uInt aRowNr, const Array* arr) { getShape(aRowNr)->putArrayStringV (*itsIosFile, arr); } void SSMIndColumn::getSliceStringV (uInt aRowNr, const Slicer& ns, Array* arr) { getShape(aRowNr)->getSliceStringV (*itsIosFile, ns, arr); } void SSMIndColumn::putSliceStringV (uInt aRowNr, const Slicer& ns, const Array* arr) { getShape(aRowNr)->putSliceStringV (*itsIosFile, ns, arr); } #define SSMIndColumn_GETPUT(T,NM) \ void SSMIndColumn::aips_name2(getArray,NM) (uInt aRowNr, Array* arr) \ { getShape(aRowNr)->aips_name2(getArray,NM) (*itsIosFile, arr); } \ void SSMIndColumn::aips_name2(putArray,NM) (uInt aRowNr, const Array* arr) \ { getShape(aRowNr)->aips_name2(putArray,NM) \ (*itsIosFile, arr); } \ void SSMIndColumn::aips_name2(getSlice,NM) \ (uInt aRowNr, const Slicer& ns, Array* arr) \ { getShape(aRowNr)->aips_name2(getSlice,NM) (*itsIosFile, ns, arr); } \ void SSMIndColumn::aips_name2(putSlice,NM) \ (uInt aRowNr, const Slicer& ns, const Array* arr) \ { getShape(aRowNr)->aips_name2(putSlice,NM) (*itsIosFile, ns, arr); } SSMIndColumn_GETPUT(Bool,BoolV) SSMIndColumn_GETPUT(uChar,uCharV) SSMIndColumn_GETPUT(Short,ShortV) SSMIndColumn_GETPUT(uShort,uShortV) SSMIndColumn_GETPUT(Int,IntV) SSMIndColumn_GETPUT(uInt,uIntV) SSMIndColumn_GETPUT(float,floatV) SSMIndColumn_GETPUT(double,doubleV) SSMIndColumn_GETPUT(Complex,ComplexV) SSMIndColumn_GETPUT(DComplex,DComplexV) 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-2.4.1/tables/DataMan/SSMIndColumn.h000066400000000000000000000247241321422335000205050ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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(); // 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); // It can handle access to a slice in a cell. virtual Bool canAccessSlice (Bool& reask) const; // Add (newNrrow-oldNrrow) rows to the column. virtual void addRow (uInt aNewNrRows, uInt 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 (uInt aRowNr); // Set the shape of the array in the given row and allocate the array // in the file. void setShape (uInt aRowNr, const IPosition& aShape); // Is the shape defined (i.e. is there an array) in this row? virtual Bool isShapeDefined (uInt aRowNr); // Get the shape of the array in the given row. virtual IPosition shape (uInt 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 getArrayBoolV (uInt aRowNr, Array* aDataPtr); virtual void getArrayuCharV (uInt aRowNr, Array* aDataPtr); virtual void getArrayShortV (uInt aRowNr, Array* aDataPtr); virtual void getArrayuShortV (uInt aRowNr, Array* aDataPtr); virtual void getArrayIntV (uInt aRowNr, Array* aDataPtr); virtual void getArrayuIntV (uInt aRowNr, Array* aDataPtr); virtual void getArrayfloatV (uInt aRowNr, Array* aDataPtr); virtual void getArraydoubleV (uInt aRowNr, Array* aDataPtr); virtual void getArrayComplexV (uInt aRowNr, Array* aDataPtr); virtual void getArrayDComplexV (uInt aRowNr, Array* aDataPtr); virtual void getArrayStringV (uInt aRowNr, Array* 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 putArrayBoolV (uInt aRowNr, const Array* aDataPtr); virtual void putArrayuCharV (uInt aRowNr, const Array* aDataPtr); virtual void putArrayShortV (uInt aRowNr, const Array* aDataPtr); virtual void putArrayuShortV (uInt aRowNr, const Array* aDataPtr); virtual void putArrayIntV (uInt aRowNr, const Array* aDataPtr); virtual void putArrayuIntV (uInt aRowNr, const Array* aDataPtr); virtual void putArrayfloatV (uInt aRowNr, const Array* aDataPtr); virtual void putArraydoubleV (uInt aRowNr, const Array* aDataPtr); virtual void putArrayComplexV (uInt aRowNr, const Array* aDataPtr); virtual void putArrayDComplexV (uInt aRowNr, const Array* aDataPtr); virtual void putArrayStringV (uInt aRowNr, const Array* 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 getSliceBoolV (uInt aRowNr, const Slicer&, Array* aDataPtr); virtual void getSliceuCharV (uInt aRowNr, const Slicer&, Array* aDataPtr); virtual void getSliceShortV (uInt aRowNr, const Slicer&, Array* aDataPtr); virtual void getSliceuShortV (uInt aRowNr, const Slicer&, Array* aDataPtr); virtual void getSliceIntV (uInt aRowNr, const Slicer&, Array* aDataPtr); virtual void getSliceuIntV (uInt aRowNr, const Slicer&, Array* aDataPtr); virtual void getSlicefloatV (uInt aRowNr, const Slicer&, Array* aDataPtr); virtual void getSlicedoubleV (uInt aRowNr, const Slicer&, Array* aDataPtr); virtual void getSliceComplexV (uInt aRowNr, const Slicer&, Array* aDataPtr); virtual void getSliceDComplexV (uInt aRowNr, const Slicer&, Array* aDataPtr); virtual void getSliceStringV (uInt aRowNr, const Slicer&, Array* 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 putSliceBoolV (uInt aRowNr, const Slicer&, const Array* aDataPtr); virtual void putSliceuCharV (uInt aRowNr, const Slicer&, const Array* aDataPtr); virtual void putSliceShortV (uInt aRowNr, const Slicer&, const Array* aDataPtr); virtual void putSliceuShortV (uInt aRowNr, const Slicer&, const Array* aDataPtr); virtual void putSliceIntV (uInt aRowNr, const Slicer&, const Array* aDataPtr); virtual void putSliceuIntV (uInt aRowNr, const Slicer&, const Array* aDataPtr); virtual void putSlicefloatV (uInt aRowNr, const Slicer&, const Array* aDataPtr); virtual void putSlicedoubleV (uInt aRowNr, const Slicer&, const Array* aDataPtr); virtual void putSliceComplexV (uInt aRowNr, const Slicer&, const Array* aDataPtr); virtual void putSliceDComplexV (uInt aRowNr, const Slicer&, const Array* aDataPtr); virtual void putSliceStringV (uInt aRowNr, const Slicer&, const Array* aDataPtr); // // Let the column object create its array file. virtual void doCreate (uInt aNrRows); // Open an existing file. virtual void getFile (uInt aNrRows); // Remove the given row from the data bucket and possibly string bucket. virtual void deleteRow(uInt aRowNr); private: // Forbid copy constructor. SSMIndColumn (const SSMIndColumn&); // Forbid assignment. SSMIndColumn& operator= (const SSMIndColumn&); // 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 (uInt aRowNr); // Return a pointer to the array in the given row (for a get). StIndArray* getArrayPtr (uInt 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-2.4.1/tables/DataMan/SSMIndStringColumn.cc000066400000000000000000000102221321422335000220160ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (uInt 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 (uInt 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 (uInt aRowNr) { if (itsShape.nelements() != 0) { return True; } else { Int buf[3]; getRowValue(buf, aRowNr); return buf[2] != 0; } } uInt SSMIndStringColumn::ndim (uInt aRowNr) { return shape(aRowNr).nelements(); } void SSMIndStringColumn::getArrayStringV (uInt aRowNr, Array* aDataPtr) { if (itsShape.nelements() != 0) { SSMDirColumn::getArrayStringV(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()); } else { itsSSMPtr->getStringHandler()->get(*aDataPtr, buf[0], buf[1], buf[2], True); } } } void SSMIndStringColumn::putArrayStringV (uInt aRowNr, const Array* aDataPtr) { if (itsShape.nelements() != 0) { SSMDirColumn::putArrayStringV(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], *aDataPtr, True); putValue(aRowNr, buf); } } } //# NAMESPACE CASACORE - END casacore-2.4.1/tables/DataMan/SSMIndStringColumn.h000066400000000000000000000103731321422335000216670ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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(); // Get an array value in the given row. // An exception is thrown if no array is defined in this row. virtual void getArrayStringV (uInt rownr, Array* dataPtr); // Put an array value in the given row. virtual void putArrayStringV (uInt rownr, const Array* dataPtr); // Set the shape of the array in the given row. void setShape (uInt aRowNr, const IPosition& aShape); // Get the shape of the array in the given row. virtual IPosition shape (uInt 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 (uInt aRowNr); // Get the dimensionality of the item in the given row. virtual uInt ndim (uInt aRowNr); private: // Forbid copy constructor. SSMIndStringColumn (const SSMIndStringColumn&); // Forbid assignment. SSMIndStringColumn& operator= (const SSMIndStringColumn&); }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/tables/DataMan/SSMIndex.cc000066400000000000000000000214321321422335000200130ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# $Id$ #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), itsFreeSpace (0), itsRowsPerBucket (rowsPerBucket), itsNrColumns (0) { } SSMIndex::~SSMIndex() { } void SSMIndex::get (AipsIO& anOs) { anOs.getstart("SSMIndex"); anOs >> itsNUsed; anOs >> itsRowsPerBucket; anOs >> itsNrColumns; anOs >> itsFreeSpace; getBlock (anOs, itsLastRow); getBlock (anOs, itsBucketNumber); anOs.getend(); } void SSMIndex::put (AipsIO& anOs) const { anOs.putstart("SSMIndex", 1); anOs << itsNUsed; anOs << itsRowsPerBucket; anOs << itsNrColumns; anOs << itsFreeSpace; putBlock (anOs, itsLastRow, itsNUsed); putBlock (anOs, itsBucketNumber, itsNUsed); anOs.putend(); } void SSMIndex::showStatistics (ostream& anOs) const { anOs << "Index statistics: " << endl; anOs << "Entries used : " << itsNUsed << endl; anOs << "Rows Per bucket : " << itsRowsPerBucket << endl; anOs << "Nr of Columns : " << itsNrColumns << endl; if (itsNrColumns > 0 ) { for (uInt i=0;i < itsNUsed; i++) { anOs << "BucketNr["<getBucketSize()-aSizeUsed; if (nfree > 0) { itsFreeSpace.define (aSizeUsed, nfree); } } void SSMIndex::addRow (uInt aNrRows) { uInt lastRow=0; if (aNrRows == 0 ) { return; } if (itsNUsed > 0 ) { lastRow = itsLastRow[itsNUsed-1]+1; uInt usedLast = lastRow; if (itsNUsed > 1) { usedLast -= itsLastRow[itsNUsed-2]+1; } uInt fitLast = itsRowsPerBucket-usedLast; uInt toAdd = 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 = min (aNrRows, itsRowsPerBucket); lastRow += toAdd; aNrRows -= toAdd; itsLastRow[itsNUsed] = lastRow-1; itsNUsed += 1; } } Int SSMIndex::deleteRow (uInt 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 (uInt 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.define(anOffset,aLength); itsNrColumns--; AlwaysAssert (itsNrColumns > -1, AipsError); uInt i=0; while (i < itsFreeSpace.ndefined()-1) { // See if space can be combined // That is possible if this and the next entry are adjacent. Int aK = itsFreeSpace.getKey(i); Int aV = itsFreeSpace.getVal(i); if (aK+aV == itsFreeSpace.getKey(i+1) ) { aV += itsFreeSpace.getVal(i+1); itsFreeSpace.remove(itsFreeSpace.getKey(i+1)); itsFreeSpace.define(aK,aV); } else { i++; } } 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 (uInt i=0; i < itsFreeSpace.ndefined(); i++) { Int aV = itsFreeSpace.getVal(i); if (aV == aLength) { anOffset = itsFreeSpace.getKey(i); //Perfect fit found, don't need to continue return(0); } if (aV >= aLength && (aV < bestLength || bestLength == -1)) { bestLength = aV; anOffset = itsFreeSpace.getKey(i); } } 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(anOffset); itsNrColumns++; itsFreeSpace.remove(anOffset); if (aLength != aV) { DebugAssert (aLength < aV, AipsError); // more space then needed, remap extra space Int anO = anOffset + aLength; aV -= aLength; itsFreeSpace.define(anO, aV); } } void SSMIndex::find (uInt aRowNumber, uInt& aBucketNr, uInt& aStartRow, uInt& 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-2.4.1/tables/DataMan/SSMIndex.h000066400000000000000000000136671321422335000176700ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# $Id$ #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 (uInt 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. Int deleteRow (uInt 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 (uInt aRowNumber, uInt& aBucketNr, uInt& aStartRow, uInt& anEndRow, const String& colName) const; private: // Get the index of the bucket containing the given row. uInt getIndex (uInt 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). SimpleOrderedMap 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-2.4.1/tables/DataMan/SSMStringHandler.cc000066400000000000000000000366561321422335000215260ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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, 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, 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, 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, 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-2.4.1/tables/DataMan/SSMStringHandler.h000066400000000000000000000244461321422335000213620ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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(); // 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: // Forbid copy constructor and assignment. // SSMStringHandler (const SSMStringHandler&); SSMStringHandler& operator= (const SSMStringHandler&); // // 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-2.4.1/tables/DataMan/ScaledArrayEngine.h000066400000000000000000000323221321422335000215430ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (uInt 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(); // 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&); // Assignment is not needed and therefore forbidden // (so it is made private and not implemented). ScaledArrayEngine& operator= (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 create (uInt 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 (uInt rownr, Array& array); // Put an array in the given row. // This will scale and offset to the underlying array. void putArray (uInt 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 (uInt 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 (uInt 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 (uInt rownr); // Get the offset value for this row. VirtualType getOffset (uInt 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-2.4.1/tables/DataMan/ScaledArrayEngine.tcc000066400000000000000000000310721321422335000220660ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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::create (uInt initialNrrow) { BaseMappedArrayEngine::create (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 (uInt rownr) { if (fixedScale_p) { return scale_p; } return (*scaleColumn_p)(rownr); } template S ScaledArrayEngine::getOffset (uInt 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); uInt 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); uInt rownr = 0; while (! arrayIter.pastEnd()) { scaleOnPut (getScale(rownr), getOffset(rownr), arrayIter.array(), targetIter.array()); rownr++; arrayIter.next(); targetIter.next(); } } } template void ScaledArrayEngine::getArray (uInt rownr, Array& array) { Array target(array.shape()); column().get (rownr, target); scaleOnGet (getScale(rownr), getOffset(rownr), array, target); } template void ScaledArrayEngine::putArray (uInt rownr, const Array& array) { Array target(array.shape()); scaleOnPut (getScale(rownr), getOffset(rownr), array, target); column().put (rownr, target); } template void ScaledArrayEngine::getSlice (uInt 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 (uInt 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-2.4.1/tables/DataMan/ScaledComplexData.h000066400000000000000000000413721321422335000215450ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (uInt 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(); // 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(); // The engine can access column cells. virtual Bool canAccessArrayColumnCells (Bool& reask) const; // 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&); // Assignment is not needed and therefore forbidden // (so it is made private and not implemented). ScaledComplexData& operator= (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 create (uInt 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 (uInt rownr, const IPosition& shape); // Get the dimensionality of the array in the given row. virtual uInt ndim (uInt 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 (uInt rownr); // Get an array in the given row. // This will scale and offset from the underlying array. virtual void getArray (uInt rownr, Array& array); // Put an array in the given row. // This will scale and offset to the underlying array. virtual void putArray (uInt 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 (uInt 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 (uInt 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 (uInt rownr); // Get the offset value for this row. VirtualType getOffset (uInt 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-2.4.1/tables/DataMan/ScaledComplexData.tcc000066400000000000000000000433011321422335000220610ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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::create (uInt initialNrrow) { BaseMappedArrayEngine::create (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); } } template Bool ScaledComplexData::canAccessArrayColumnCells (Bool& reask) const { reask = False; return True; } //# 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 (uInt rownr, const IPosition& shape) { BaseMappedArrayEngine::setShape (rownr, storedShape(shape)); } template uInt ScaledComplexData::ndim (uInt rownr) { return column().ndim (rownr) - 1; } template IPosition ScaledComplexData::shape (uInt 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 (uInt rownr) { if (fixedScale_p) { return scale_p; } return (*scaleColumn_p)(rownr); } template S ScaledComplexData::getOffset (uInt 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); uInt 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); uInt 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()) { uInt rownr = rowiter.sliceStart(); uInt end = rowiter.sliceEnd(); uInt 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()) { uInt rownr = rowiter.sliceStart(); uInt end = rowiter.sliceEnd(); uInt 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 (uInt rownr, Array& array) { Array target(storedShape(array.shape())); column().get (rownr, target); scaleOnGet (getScale(rownr), getOffset(rownr), array, target); } template void ScaledComplexData::putArray (uInt rownr, const Array& array) { Array target(storedShape(array.shape())); scaleOnPut (getScale(rownr), getOffset(rownr), array, target); column().put (rownr, target); } template void ScaledComplexData::getSlice (uInt 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 (uInt 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-2.4.1/tables/DataMan/StArrAipsIO.cc000066400000000000000000000272711321422335000204700ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include #include #include #include #include #include #include #include #include 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* arr) { Bool deleteIt; float* data = arr->getStorage (deleteIt); objcopy (data, (const float*)(getArrayPtr (rownr)), nrelem_p); arr->putStorage (data, deleteIt); } void StManColumnArrayAipsIO::putArrayfloatV (uInt rownr, const Array* arr) { Bool deleteIt; const float* data = arr->getStorage (deleteIt); objcopy ((float*)(getArrayPtr (rownr)), data, nrelem_p); arr->freeStorage (data, deleteIt); stmanPtr_p->setHasPut(); } void StManColumnArrayAipsIO::getSlicefloatV (uInt rownr, const Slicer& ns, Array* arr) { Array tabarr (shape_p, (float*) (getArrayPtr (rownr)), SHARE); IPosition blc, trc, inc; ns.inferShapeFromSource (shape_p, blc, trc, inc); *arr = tabarr(blc, trc, inc); } void StManColumnArrayAipsIO::putSlicefloatV (uInt rownr, const Slicer& ns, const Array* arr) { Array tabarr (shape_p, (float*) (getArrayPtr (rownr)), SHARE); IPosition blc, trc, inc; ns.inferShapeFromSource (shape_p, blc, trc, inc); tabarr(blc, trc, inc) = *arr; stmanPtr_p->setHasPut(); } #define STMANCOLUMNARRAYAIPSIO_GETPUT(T,NM) \ void StManColumnArrayAipsIO::aips_name2(getArray,NM) (uInt rownr, \ Array* arr) \ { \ Bool deleteIt; \ T* data = arr->getStorage (deleteIt); \ objcopy (data, (const T*)(getArrayPtr (rownr)), nrelem_p); \ arr->putStorage (data, deleteIt); \ } \ void StManColumnArrayAipsIO::aips_name2(putArray,NM) (uInt rownr, \ const Array* arr) \ { \ Bool deleteIt; \ const T* data = arr->getStorage (deleteIt); \ objcopy ((T*)(getArrayPtr (rownr)), data, nrelem_p); \ arr->freeStorage (data, deleteIt); \ stmanPtr_p->setHasPut(); \ } \ void StManColumnArrayAipsIO::aips_name2(getSlice,NM) \ (uInt rownr, const Slicer& ns, Array* arr) \ { \ Array tabarr (shape_p, (T*) (getArrayPtr (rownr)), SHARE); \ IPosition blc, trc, inc; \ ns.inferShapeFromSource (shape_p, blc, trc, inc); \ *arr = tabarr(blc, trc, inc); \ } \ void StManColumnArrayAipsIO::aips_name2(putSlice,NM) \ (uInt rownr, const Slicer& ns, const Array* arr) \ { \ Array tabarr (shape_p, (T*) (getArrayPtr (rownr)), SHARE); \ IPosition blc, trc, inc; \ ns.inferShapeFromSource (shape_p, blc, trc, inc); \ tabarr(blc, trc, inc) = *arr; \ stmanPtr_p->setHasPut(); \ } STMANCOLUMNARRAYAIPSIO_GETPUT(Bool,BoolV) STMANCOLUMNARRAYAIPSIO_GETPUT(uChar,uCharV) STMANCOLUMNARRAYAIPSIO_GETPUT(Short,ShortV) STMANCOLUMNARRAYAIPSIO_GETPUT(uShort,uShortV) STMANCOLUMNARRAYAIPSIO_GETPUT(Int,IntV) STMANCOLUMNARRAYAIPSIO_GETPUT(uInt,uIntV) //#//STMANCOLUMNARRAYAIPSIO_GETPUT(float,floatV) STMANCOLUMNARRAYAIPSIO_GETPUT(double,doubleV) STMANCOLUMNARRAYAIPSIO_GETPUT(Complex,ComplexV) STMANCOLUMNARRAYAIPSIO_GETPUT(DComplex,DComplexV) STMANCOLUMNARRAYAIPSIO_GETPUT(String,StringV) void StManColumnArrayAipsIO::getArrayColumnfloatV (Array* arr) { uInt nrmax = arr->shape()(arr->ndim()-1); Bool deleteItTarget; float* target = arr->getStorage (deleteItTarget); uInt nr; void* ext; uInt extnr = 0; while ((nr = nextExt (ext, extnr, nrmax)) > 0) { const float** dpa = (const float**)ext; for (uInt i=0; iputStorage (target, deleteItTarget); } void StManColumnArrayAipsIO::putArrayColumnfloatV (const Array* arr) { uInt nrmax = arr->shape()(arr->ndim()-1); Bool deleteItTarget; const float* target = arr->getStorage (deleteItTarget); uInt nr; void* ext; uInt extnr = 0; while ((nr = nextExt (ext, extnr, nrmax)) > 0) { float** dpa = (float**)ext; for (uInt i=0; ifreeStorage (target, deleteItTarget); stmanPtr_p->setHasPut(); } #define STMANCOLUMNARRAYAIPSIO_GETPUTCOLUMN(T,NM) \ void StManColumnArrayAipsIO::aips_name2(getArrayColumn,NM) \ (Array* arr) \ { \ uInt nrmax = arr->shape()(arr->ndim()-1); \ Bool deleteItTarget; \ T* target = arr->getStorage (deleteItTarget); \ uInt nr; \ void* ext; \ uInt extnr = 0; \ while ((nr = nextExt (ext, extnr, nrmax)) > 0) { \ const T** dpa = (const T**)ext; \ for (uInt i=0; iputStorage (target, deleteItTarget); \ } \ void StManColumnArrayAipsIO::aips_name2(putArrayColumn,NM) \ (const Array* arr) \ { \ uInt nrmax = arr->shape()(arr->ndim()-1); \ Bool deleteItTarget; \ const T* target = arr->getStorage (deleteItTarget); \ uInt nr; \ void* ext; \ uInt extnr = 0; \ while ((nr = nextExt (ext, extnr, nrmax)) > 0) { \ T** dpa = (T**)ext; \ for (uInt i=0; ifreeStorage (target, deleteItTarget); \ stmanPtr_p->setHasPut(); \ } STMANCOLUMNARRAYAIPSIO_GETPUTCOLUMN(Bool,BoolV) STMANCOLUMNARRAYAIPSIO_GETPUTCOLUMN(uChar,uCharV) STMANCOLUMNARRAYAIPSIO_GETPUTCOLUMN(Short,ShortV) STMANCOLUMNARRAYAIPSIO_GETPUTCOLUMN(uShort,uShortV) STMANCOLUMNARRAYAIPSIO_GETPUTCOLUMN(Int,IntV) STMANCOLUMNARRAYAIPSIO_GETPUTCOLUMN(uInt,uIntV) //#//STMANCOLUMNARRAYAIPSIO_GETPUTCOLUMN(float,floatV) STMANCOLUMNARRAYAIPSIO_GETPUTCOLUMN(double,doubleV) STMANCOLUMNARRAYAIPSIO_GETPUTCOLUMN(Complex,ComplexV) STMANCOLUMNARRAYAIPSIO_GETPUTCOLUMN(DComplex,DComplexV) STMANCOLUMNARRAYAIPSIO_GETPUTCOLUMN(String,StringV) void StManColumnArrayAipsIO::remove (uInt rownr) { deleteArray (rownr); StManColumnAipsIO::remove (rownr); } Bool StManColumnArrayAipsIO::ok() const { return StManColumnAipsIO::ok(); } void StManColumnArrayAipsIO::deleteArray (uInt rownr) { void* datap = getArrayPtr (rownr); deleteData (datap, False); } //# Write all data into AipsIO. void StManColumnArrayAipsIO::putFile (uInt 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 (uInt nrval, AipsIO& ios) { uInt version = ios.getstart ("StManColumnArrayAipsIO"); if (version == 1) { IPosition shape; uInt n; ios >> n; // data type ios >> shape_p; 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_p) { 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 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; } } #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_p) { 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 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; } } } //# NAMESPACE CASACORE - END casacore-2.4.1/tables/DataMan/StArrAipsIO.h000066400000000000000000000267301321422335000203310ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef TABLES_STARRAIPSIO_H #define TABLES_STARRAIPSIO_H //# Includes #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. ~StManColumnArrayAipsIO(); // It can handle access to a slice in a cell. Bool canAccessSlice (Bool& reask) const; // It can handle access to an entire column. Bool canAccessArrayColumn (Bool& reask) const; // Set the shape of the arrays in the entire column. void setShapeColumn (const IPosition& shape); // Add (newNrrow-oldNrrow) rows to the column. // Allocate the data arrays in these rows. void addRow (uInt newNrrow, uInt oldNrrow); // Get the dimensionality of the item in the given row. // This is the same for all rows. uInt ndim (uInt rownr); // Get the shape of the array in the given row. // This is the same for all rows. IPosition shape (uInt 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). // void getArrayBoolV (uInt rownr, Array* dataPtr); void getArrayuCharV (uInt rownr, Array* dataPtr); void getArrayShortV (uInt rownr, Array* dataPtr); void getArrayuShortV (uInt rownr, Array* dataPtr); void getArrayIntV (uInt rownr, Array* dataPtr); void getArrayuIntV (uInt rownr, Array* dataPtr); void getArrayfloatV (uInt rownr, Array* dataPtr); void getArraydoubleV (uInt rownr, Array* dataPtr); void getArrayComplexV (uInt rownr, Array* dataPtr); void getArrayDComplexV (uInt rownr, Array* dataPtr); void getArrayStringV (uInt rownr, Array* 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). // void putArrayBoolV (uInt rownr, const Array* dataPtr); void putArrayuCharV (uInt rownr, const Array* dataPtr); void putArrayShortV (uInt rownr, const Array* dataPtr); void putArrayuShortV (uInt rownr, const Array* dataPtr); void putArrayIntV (uInt rownr, const Array* dataPtr); void putArrayuIntV (uInt rownr, const Array* dataPtr); void putArrayfloatV (uInt rownr, const Array* dataPtr); void putArraydoubleV (uInt rownr, const Array* dataPtr); void putArrayComplexV (uInt rownr, const Array* dataPtr); void putArrayDComplexV (uInt rownr, const Array* dataPtr); void putArrayStringV (uInt rownr, const Array* 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). // void getSliceBoolV (uInt rownr, const Slicer&, Array* dataPtr); void getSliceuCharV (uInt rownr, const Slicer&, Array* dataPtr); void getSliceShortV (uInt rownr, const Slicer&, Array* dataPtr); void getSliceuShortV (uInt rownr, const Slicer&, Array* dataPtr); void getSliceIntV (uInt rownr, const Slicer&, Array* dataPtr); void getSliceuIntV (uInt rownr, const Slicer&, Array* dataPtr); void getSlicefloatV (uInt rownr, const Slicer&, Array* dataPtr); void getSlicedoubleV (uInt rownr, const Slicer&, Array* dataPtr); void getSliceComplexV (uInt rownr, const Slicer&, Array* dataPtr); void getSliceDComplexV (uInt rownr, const Slicer&, Array* dataPtr); void getSliceStringV (uInt rownr, const Slicer&, Array* 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). // void putSliceBoolV (uInt rownr, const Slicer&, const Array* dataPtr); void putSliceuCharV (uInt rownr, const Slicer&, const Array* dataPtr); void putSliceShortV (uInt rownr, const Slicer&, const Array* dataPtr); void putSliceuShortV (uInt rownr, const Slicer&, const Array* dataPtr); void putSliceIntV (uInt rownr, const Slicer&, const Array* dataPtr); void putSliceuIntV (uInt rownr, const Slicer&, const Array* dataPtr); void putSlicefloatV (uInt rownr, const Slicer&, const Array* dataPtr); void putSlicedoubleV (uInt rownr, const Slicer&, const Array* dataPtr); void putSliceComplexV (uInt rownr, const Slicer&, const Array* dataPtr); void putSliceDComplexV (uInt rownr, const Slicer&, const Array* dataPtr); void putSliceStringV (uInt rownr, const Slicer&, const Array* dataPtr); // // Get all array values in the column. // The buffer pointed to by dataPtr has to have the correct length // (which is guaranteed by the ArrayColumn getColumn function). // void getArrayColumnBoolV (Array* dataPtr); void getArrayColumnuCharV (Array* dataPtr); void getArrayColumnShortV (Array* dataPtr); void getArrayColumnuShortV (Array* dataPtr); void getArrayColumnIntV (Array* dataPtr); void getArrayColumnuIntV (Array* dataPtr); void getArrayColumnfloatV (Array* dataPtr); void getArrayColumndoubleV (Array* dataPtr); void getArrayColumnComplexV (Array* dataPtr); void getArrayColumnDComplexV (Array* dataPtr); void getArrayColumnStringV (Array* dataPtr); // // Put all arrays in the column. // The buffer pointed to by dataPtr has to have the correct length // (which is guaranteed by the ArrayColumn putColumn function). // void putArrayColumnBoolV (const Array* dataPtr); void putArrayColumnuCharV (const Array* dataPtr); void putArrayColumnShortV (const Array* dataPtr); void putArrayColumnuShortV (const Array* dataPtr); void putArrayColumnIntV (const Array* dataPtr); void putArrayColumnuIntV (const Array* dataPtr); void putArrayColumnfloatV (const Array* dataPtr); void putArrayColumndoubleV (const Array* dataPtr); void putArrayColumnComplexV (const Array* dataPtr); void putArrayColumnDComplexV (const Array* dataPtr); void putArrayColumnStringV (const Array* dataPtr); // // Remove the value at the given index. void remove (uInt index); // 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. void putFile (uInt 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. void getFile (uInt nrval, AipsIO&); // Check if the class invariants still hold. Bool ok() const; private: // The data type of the array (as defined in DataType.h). int dtypeArr_p; // 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 (uInt index); // Put the data of a data block. // datap is an array of nrval pointers to arrays. 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]. void getData (void* datap, uInt index, uInt nrval, AipsIO&, uInt version); // Forbid copy constructor. StManColumnArrayAipsIO (const StManColumnArrayAipsIO&); // Forbid assignment. StManColumnArrayAipsIO& operator= (const StManColumnArrayAipsIO&); }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/tables/DataMan/StArrayFile.cc000066400000000000000000000350471321422335000205550ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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, MultiFileBase* 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 = new MFFileIO (*mfile, fname, fop); } else { file_p = new RegularFileIO (RegularFile(fname), fop, bufferSize); } if (bigEndian) { iofil_p = new CanonicalIO (file_p); }else{ iofil_p = 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); sizeFloat_p = ValType::getCanonicalSize (TpFloat, bigEndian); sizeDouble_p = ValType::getCanonicalSize (TpDouble, bigEndian); if (bigEndian) { sizeInt64_p = CanonicalConversion::canonicalSize(static_cast(0)); sizeuInt64_p = CanonicalConversion::canonicalSize(static_cast(0)); } else { sizeInt64_p = LECanonicalConversion::canonicalSize(static_cast(0)); sizeuInt64_p = LECanonicalConversion::canonicalSize(static_cast(0)); } } //# Close the file. //# Delete it if required. StManArrayFile::~StManArrayFile () { //# Write the version and file length at the beginning. flush (False); delete iofil_p; delete file_p; } 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, uInt arrayOff, uInt nr, const Float* data) { setpos (fileOff + Int64(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, uInt arrayOff, uInt nr, Float* data) { setpos (fileOff + Int64(arrayOff)*sizeFloat_p); iofil_p->read (nr, data); } void StManArrayFile::copyArrayFloat (Int64 to, Int64 from, uInt nr) { copyData (to, from, nr*sizeFloat_p); } //# Put a vector at the given offset. #define STMANARRAYFILE_PUTGET(T,SIZEDTYPE) \ void StManArrayFile::put (Int64 fileOff, uInt arrayOff, uInt nr, \ const T* data) \ { \ setpos (fileOff + Int64(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, uInt arrayOff, uInt nr, T* data) \ { \ setpos (fileOff + Int64(arrayOff)*SIZEDTYPE); \ iofil_p->read (nr, data); \ } \ void StManArrayFile::aips_name2(copyArray,T) (Int64 to, Int64 from, uInt 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, uInt arrayOff, uInt nr, const Bool* data) { //# Bools are stored as bits, thus a bit more complex. uInt start = arrayOff / 8; uInt stbit = arrayOff - 8 * start; uInt 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, uInt arrayOff, uInt nr, Bool* data) { //# Bools are stored as bits, thus a bit more complex. uInt start = arrayOff / 8; uInt stbit = arrayOff - 8 * start; uInt 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, uInt 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); uInt 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, uInt arrayOff, uInt nr, const Complex* data) { setpos (fileOff + Int64(arrayOff)*2*sizeFloat_p); iofil_p->write (2*nr, (const Float*)data); hasPut_p = True; } void StManArrayFile::put (Int64 fileOff, uInt arrayOff, uInt nr, const DComplex* data) { setpos (fileOff + Int64(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, uInt arrayOff, uInt nr, const String* data) { //# Get file offset for string offset array. //# Allocate a buffer to hold 4096 string offsets. Int64 offs = fileOff + Int64(arrayOff)*sizeuInt_p; uInt buf[4096]; uInt i, n; while (nr > 0) { n = (nr < 4096 ? nr : 4096); setpos (leng_p); // position at end of file for (i=0; ilength into an uInt so // the SGI compiler could find the right overloaded function // some problem with string::size_t conversion // leng_p += put (uInt(data->length())); // 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, uInt arrayOff, uInt nr, Complex* data) { setpos (fileOff + Int64(arrayOff)*2*sizeFloat_p); iofil_p->read (2*nr, (Float*)data); } void StManArrayFile::get (Int64 fileOff, uInt arrayOff, uInt nr, DComplex* data) { setpos (fileOff + Int64(arrayOff)*2*sizeDouble_p); iofil_p->read (2*nr, (Double*)data); } //# Get a string at the given file offset. void StManArrayFile::get (Int64 fileOff, uInt arrayOff, uInt nr, String* data) { //# Get file offset for string offset array. //# Allocate a buffer to hold 4096 string offsets. Int64 offs = fileOff + Int64(arrayOff)*sizeuInt_p; uInt buf[4096]; uInt i, n, l; while (nr > 0) { n = (nr < 4096 ? nr : 4096); setpos (offs); offs += iofil_p->read (n, buf); for (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, uInt nr) { copyData (to, from, nr*2*sizeFloat_p); } void StManArrayFile::copyArrayDComplex (Int64 to, Int64 from, uInt nr) { copyData (to, from, nr*2*sizeDouble_p); } void StManArrayFile::copyArrayString (Int64 to, Int64 from, uInt nr) { String data[4096]; uInt ndone = 0; for (uInt 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, uInt length) { uChar buffer[32768]; for (uInt 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 (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 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 canonical 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, MultiFileBase* mfile=0); // 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 long 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, uInt arrayOffset, uInt nr, const Bool*); void put (Int64 fileOffset, uInt arrayOffset, uInt nr, const Char*); void put (Int64 fileOffset, uInt arrayOffset, uInt nr, const uChar*); void put (Int64 fileOffset, uInt arrayOffset, uInt nr, const Short*); void put (Int64 fileOffset, uInt arrayOffset, uInt nr, const uShort*); void put (Int64 fileOffset, uInt arrayOffset, uInt nr, const Int*); void put (Int64 fileOffset, uInt arrayOffset, uInt nr, const uInt*); void put (Int64 fileOffset, uInt arrayOffset, uInt nr, const Int64*); void put (Int64 fileOffset, uInt arrayOffset, uInt nr, const uInt64*); void put (Int64 fileOffset, uInt arrayOffset, uInt nr, const Float*); void put (Int64 fileOffset, uInt arrayOffset, uInt nr, const Double*); //#// void put (Int64 fileOffset, uInt arrayOffset, uInt nr, const long double*); void put (Int64 fileOffset, uInt arrayOffset, uInt nr, const Complex*); void put (Int64 fileOffset, uInt arrayOffset, uInt nr, const DComplex*); void put (Int64 fileOffset, uInt arrayOffset, uInt 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, uInt arrayOffset, uInt nr, Bool*); void get (Int64 fileOffset, uInt arrayOffset, uInt nr, Char*); void get (Int64 fileOffset, uInt arrayOffset, uInt nr, uChar*); void get (Int64 fileOffset, uInt arrayOffset, uInt nr, Short*); void get (Int64 fileOffset, uInt arrayOffset, uInt nr, uShort*); void get (Int64 fileOffset, uInt arrayOffset, uInt nr, Int*); void get (Int64 fileOffset, uInt arrayOffset, uInt nr, uInt*); void get (Int64 fileOffset, uInt arrayOffset, uInt nr, Int64*); void get (Int64 fileOffset, uInt arrayOffset, uInt nr, uInt64*); void get (Int64 fileOffset, uInt arrayOffset, uInt nr, Float*); void get (Int64 fileOffset, uInt arrayOffset, uInt nr, Double*); //#// void get (Int64 fileOffset, uInt arrayOffset, uInt nr, long double*); void get (Int64 fileOffset, uInt arrayOffset, uInt nr, Complex*); void get (Int64 fileOffset, uInt arrayOffset, uInt nr, DComplex*); void get (Int64 fileOffset, uInt arrayOffset, uInt nr, String*); // // Copy the array with nr elements from one file offset // to another. // void copyArrayBool (Int64 to, Int64 from, uInt nr); void copyArrayChar (Int64 to, Int64 from, uInt nr); void copyArrayuChar (Int64 to, Int64 from, uInt nr); void copyArrayShort (Int64 to, Int64 from, uInt nr); void copyArrayuShort (Int64 to, Int64 from, uInt nr); void copyArrayInt (Int64 to, Int64 from, uInt nr); void copyArrayuInt (Int64 to, Int64 from, uInt nr); void copyArrayInt64 (Int64 to, Int64 from, uInt nr); void copyArrayuInt64 (Int64 to, Int64 from, uInt nr); void copyArrayFloat (Int64 to, Int64 from, uInt nr); void copyArrayDouble (Int64 to, Int64 from, uInt nr); //#// void copyArrayLDouble (Int64 to, Int64 from, uInt nr); void copyArrayComplex (Int64 to, Int64 from, uInt nr); void copyArrayDComplex (Int64 to, Int64 from, uInt nr); void copyArrayString (Int64 to, Int64 from, uInt nr); // private: ByteIO* file_p; //# File object TypeIO* 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, uInt 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-2.4.1/tables/DataMan/StIndArrAIO.cc000066400000000000000000000216671321422335000204120ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include #include #include #include #include #include #include #include #include //# for sprintf 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), seqnr_p (smptr->uniqueNr()), shapeIsFixed_p(False), version_p (2), iosfile_p (0) {} //# Delete all objects created. StManColumnIndArrayAipsIO::~StManColumnIndArrayAipsIO() { uInt nr = stmanPtr_p->nrow(); for (uInt i=0; i 1) { iosfile_p = stmanPtr_p->openArrayFile (opt); } else { //# Open/create the file holding the arrays in the column. if (iosfile_p == 0) { char strc[8]; sprintf (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 (uInt nrnew, uInt 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_p, 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 (uInt 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 (uInt rownr) { return (STMANINDGETBLOCK(rownr) == 0 ? False : True); } uInt StManColumnIndArrayAipsIO::ndim (uInt rownr) { return getShape(rownr)->shape().nelements(); } IPosition StManColumnIndArrayAipsIO::shape (uInt rownr) { return getShape(rownr)->shape(); } Bool StManColumnIndArrayAipsIO::canChangeShape() const { return (shapeIsFixed_p ? False : True); } Bool StManColumnIndArrayAipsIO::canAccessSlice (Bool& reask) const { reask = False; return True; } void StManColumnIndArrayAipsIO::getArrayfloatV (uInt rownr, Array* arr) { getShape(rownr)->getArrayfloatV (*iosfile_p, arr); } void StManColumnIndArrayAipsIO::putArrayfloatV (uInt rownr, const Array* arr) { getShape(rownr)->putArrayfloatV (*iosfile_p, arr); stmanPtr_p->setHasPut(); } void StManColumnIndArrayAipsIO::getSlicefloatV (uInt rownr, const Slicer& ns, Array* arr) { getShape(rownr)->getSlicefloatV (*iosfile_p, ns, arr); } void StManColumnIndArrayAipsIO::putSlicefloatV (uInt rownr, const Slicer& ns, const Array* arr) { getShape(rownr)->putSlicefloatV (*iosfile_p, ns, arr); stmanPtr_p->setHasPut(); } #define STMANCOLUMNINDARRAYAIPSIO_GETPUT(T,NM) \ void StManColumnIndArrayAipsIO::aips_name2(getArray,NM) (uInt rownr, \ Array* arr) \ { \ getShape(rownr)->aips_name2(getArray,NM) (*iosfile_p, arr); \ } \ void StManColumnIndArrayAipsIO::aips_name2(putArray,NM) (uInt rownr, \ const Array* arr) \ { \ getShape(rownr)->aips_name2(putArray,NM) (*iosfile_p, arr); \ stmanPtr_p->setHasPut(); \ } \ void StManColumnIndArrayAipsIO::aips_name2(getSlice,NM) \ (uInt rownr, const Slicer& ns, Array* arr) \ { \ getShape(rownr)->aips_name2(getSlice,NM) (*iosfile_p, ns, arr); \ } \ void StManColumnIndArrayAipsIO::aips_name2(putSlice,NM) \ (uInt rownr, const Slicer& ns, const Array* arr) \ { \ getShape(rownr)->aips_name2(putSlice,NM) (*iosfile_p, ns, arr); \ stmanPtr_p->setHasPut(); \ } STMANCOLUMNINDARRAYAIPSIO_GETPUT(Bool,BoolV) STMANCOLUMNINDARRAYAIPSIO_GETPUT(uChar,uCharV) STMANCOLUMNINDARRAYAIPSIO_GETPUT(Short,ShortV) STMANCOLUMNINDARRAYAIPSIO_GETPUT(uShort,uShortV) STMANCOLUMNINDARRAYAIPSIO_GETPUT(Int,IntV) STMANCOLUMNINDARRAYAIPSIO_GETPUT(uInt,uIntV) //#//STMANCOLUMNINDARRAYAIPSIO_GETPUT(float,floatV) STMANCOLUMNINDARRAYAIPSIO_GETPUT(double,doubleV) STMANCOLUMNINDARRAYAIPSIO_GETPUT(Complex,ComplexV) STMANCOLUMNINDARRAYAIPSIO_GETPUT(DComplex,DComplexV) STMANCOLUMNINDARRAYAIPSIO_GETPUT(String,StringV) void StManColumnIndArrayAipsIO::remove (uInt rownr) { deleteArray (rownr); StManColumnAipsIO::remove (rownr); } Bool StManColumnIndArrayAipsIO::ok() const { return StManColumnAipsIO::ok(); } void StManColumnIndArrayAipsIO::deleteArray (uInt rownr) { delete STMANINDGETBLOCK(rownr); } //# Write all data into AipsIO. void StManColumnIndArrayAipsIO::putFile (uInt nrval, AipsIO& ios) { ios.putstart ("StManColumnIndArrayAipsIO", version_p); ios << dtype_p; // 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 (uInt 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-2.4.1/tables/DataMan/StIndArrAIO.h000066400000000000000000000265371321422335000202550ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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. ~StManColumnIndArrayAipsIO(); // It can handle access to a slice in a cell. Bool canAccessSlice (Bool& reask) const; // Set the (fixed) shape of the arrays in the entire column. void setShapeColumn (const IPosition& shape); // Add (newNrrow-oldNrrow) rows to the column. // Allocate the data arrays in these rows if the shape is fixed. void addRow (uInt newNrrow, uInt oldNrrow); // Set the shape of the array in the given row and allocate the array // in the file. void setShape (uInt rownr, const IPosition& shape); // Is the shape defined (i.e. is there an array) in this row? Bool isShapeDefined (uInt rownr); // Get the dimensionality of the item in the given row. // 0 is returned if there is no array. uInt ndim (uInt rownr); // Get the shape of the array in the given row. // An zero-length IPosition is returned if there is no array. IPosition shape (uInt rownr); // This storage manager can handle changing array shapes // for non-FixedShape columns. 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). // void getArrayBoolV (uInt rownr, Array* dataPtr); void getArrayuCharV (uInt rownr, Array* dataPtr); void getArrayShortV (uInt rownr, Array* dataPtr); void getArrayuShortV (uInt rownr, Array* dataPtr); void getArrayIntV (uInt rownr, Array* dataPtr); void getArrayuIntV (uInt rownr, Array* dataPtr); void getArrayfloatV (uInt rownr, Array* dataPtr); void getArraydoubleV (uInt rownr, Array* dataPtr); void getArrayComplexV (uInt rownr, Array* dataPtr); void getArrayDComplexV (uInt rownr, Array* dataPtr); void getArrayStringV (uInt rownr, Array* 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). // void putArrayBoolV (uInt rownr, const Array* dataPtr); void putArrayuCharV (uInt rownr, const Array* dataPtr); void putArrayShortV (uInt rownr, const Array* dataPtr); void putArrayuShortV (uInt rownr, const Array* dataPtr); void putArrayIntV (uInt rownr, const Array* dataPtr); void putArrayuIntV (uInt rownr, const Array* dataPtr); void putArrayfloatV (uInt rownr, const Array* dataPtr); void putArraydoubleV (uInt rownr, const Array* dataPtr); void putArrayComplexV (uInt rownr, const Array* dataPtr); void putArrayDComplexV (uInt rownr, const Array* dataPtr); void putArrayStringV (uInt rownr, const Array* 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). // void getSliceBoolV (uInt rownr, const Slicer&, Array* dataPtr); void getSliceuCharV (uInt rownr, const Slicer&, Array* dataPtr); void getSliceShortV (uInt rownr, const Slicer&, Array* dataPtr); void getSliceuShortV (uInt rownr, const Slicer&, Array* dataPtr); void getSliceIntV (uInt rownr, const Slicer&, Array* dataPtr); void getSliceuIntV (uInt rownr, const Slicer&, Array* dataPtr); void getSlicefloatV (uInt rownr, const Slicer&, Array* dataPtr); void getSlicedoubleV (uInt rownr, const Slicer&, Array* dataPtr); void getSliceComplexV (uInt rownr, const Slicer&, Array* dataPtr); void getSliceDComplexV (uInt rownr, const Slicer&, Array* dataPtr); void getSliceStringV (uInt rownr, const Slicer&, Array* 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). // void putSliceBoolV (uInt rownr, const Slicer&, const Array* dataPtr); void putSliceuCharV (uInt rownr, const Slicer&, const Array* dataPtr); void putSliceShortV (uInt rownr, const Slicer&, const Array* dataPtr); void putSliceuShortV (uInt rownr, const Slicer&, const Array* dataPtr); void putSliceIntV (uInt rownr, const Slicer&, const Array* dataPtr); void putSliceuIntV (uInt rownr, const Slicer&, const Array* dataPtr); void putSlicefloatV (uInt rownr, const Slicer&, const Array* dataPtr); void putSlicedoubleV (uInt rownr, const Slicer&, const Array* dataPtr); void putSliceComplexV (uInt rownr, const Slicer&, const Array* dataPtr); void putSliceDComplexV (uInt rownr, const Slicer&, const Array* dataPtr); void putSliceStringV (uInt rownr, const Slicer&, const Array* dataPtr); // // Remove the value in the given row. // This will result in lost file space. void remove (uInt rownr); // Let the column create its array file. void doCreate (uInt 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. void putFile (uInt 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. void getFile (uInt 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 (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 (uInt rownr); // Read the shape at the given row. // This will cache the information in the StIndArray // object for that row. StIndArray* getShape (uInt 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); // Forbid copy constructor. StManColumnIndArrayAipsIO (const StManColumnIndArrayAipsIO&); // Forbid assignment. StManColumnIndArrayAipsIO& operator= (const StManColumnIndArrayAipsIO&); }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/tables/DataMan/StIndArray.cc000066400000000000000000000331321321422335000204010ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 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 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; } } void StIndArray::checkShape (const IPosition& userArrayShape, const IPosition& tableArrayShape) const { if (! userArrayShape.isEqual (tableArrayShape)) { throw (DataManInternalError ("StManIndArray::get/put shapes not conforming")); } } void StIndArray::getArrayfloatV (StManArrayFile& ios, Array* arr) { checkShape (arr->shape(), shape_p); Bool deleteIt; float* value = arr->getStorage (deleteIt); ios.get (fileOffset_p + arrOffset_p, 0, shape_p.product(), value); arr->putStorage (value, deleteIt); } void StIndArray::putArrayfloatV (StManArrayFile& ios, const Array* arr) { checkShape (arr->shape(), shape_p); Bool deleteIt; const float* value = arr->getStorage (deleteIt); ios.put (fileOffset_p + arrOffset_p, 0, shape_p.product(), value); arr->freeStorage (value, deleteIt); } void StIndArray::getSlicefloatV (StManArrayFile& ios, const Slicer& ns, Array* arr) { Bool deleteIt; float* value = arr->getStorage (deleteIt); getSliceData (ios, ns, value, arr->shape(), &StIndArray::getVecfloatV); arr->putStorage (value, deleteIt); } void StIndArray::getVecfloatV (StManArrayFile& ios, Int64 fileOffset, uInt start, uInt leng, uInt inc, uInt 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, uInt, uInt, uInt, uInt, 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. uInt 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); uInt i, offset; uInt 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(); offset = 0; for (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::putSlicefloatV (StManArrayFile& ios, const Slicer& ns, const Array* arr) { Bool deleteIt; const float* value = arr->getStorage (deleteIt); putSliceData (ios, ns, value, arr->shape(), &StIndArray::putVecfloatV); arr->freeStorage (value, deleteIt); } void StIndArray::putVecfloatV (StManArrayFile& ios, Int64 fileOffset, uInt start, uInt leng, uInt inc, uInt 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, uInt, uInt, uInt, uInt, 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); uInt 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); uInt i, offset; uInt count=0; while (! iter.pastEnd()) { pos = iter.pos(); offset = 0; for (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(getArray,NM) (StManArrayFile& ios, Array* arr) \ { \ checkShape (arr->shape(), shape_p); \ Bool deleteIt; \ T* value = arr->getStorage (deleteIt); \ ios.get (fileOffset_p + arrOffset_p, \ 0, shape_p.product(), value); \ arr->putStorage (value, deleteIt); \ } \ void StIndArray::aips_name2(putArray,NM) (StManArrayFile& ios, \ const Array* arr) \ { \ checkShape (arr->shape(), shape_p); \ Bool deleteIt; \ const T* value = arr->getStorage (deleteIt); \ ios.put (fileOffset_p + arrOffset_p, \ 0, shape_p.product(), value); \ arr->freeStorage (value, deleteIt); \ } \ void StIndArray::aips_name2(getSlice,NM) (StManArrayFile& ios, \ const Slicer& ns, Array* arr) \ { \ Bool deleteIt; \ T* value = arr->getStorage (deleteIt); \ getSliceData (ios, ns, value, arr->shape(), \ &StIndArray::aips_name2(getVec,NM)); \ arr->putStorage (value, deleteIt); \ } \ void StIndArray::aips_name2(getVec,NM) \ (StManArrayFile& ios, Int64 fileOffset, \ uInt start, uInt leng, uInt inc, uInt 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(putSlice,NM) (StManArrayFile& ios, \ const Slicer& ns, \ const Array* arr) \ { \ Bool deleteIt; \ const T* value = arr->getStorage (deleteIt); \ putSliceData (ios, ns, value, arr->shape(), \ &StIndArray::aips_name2(putVec,NM)); \ arr->freeStorage (value, deleteIt); \ } \ void StIndArray::aips_name2(putVec,NM) \ (StManArrayFile& ios, Int64 fileOffset, \ uInt start, uInt leng, uInt inc, uInt 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(float,floatV) STINDARRAY_GETPUT(double,doubleV) STINDARRAY_GETPUT(Complex,ComplexV) STINDARRAY_GETPUT(DComplex,DComplexV) STINDARRAY_GETPUT(String,StringV) } //# NAMESPACE CASACORE - END casacore-2.4.1/tables/DataMan/StIndArray.h000066400000000000000000000421701321422335000202450ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef TABLES_STINDARRAY_H #define TABLES_STINDARRAY_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class Slicer; template class Array; // // 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 StmanColumnIndArrayAipsIO and // StManColumnIndArrayMirAIO (the AipsIO and Miriad-like storage // manager, resp.), but it is not limited to them. It can equally // well be used for 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 when 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. // When 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 getArrayBoolV (StManArrayFile&, Array* dataPtr); void getArrayuCharV (StManArrayFile&, Array* dataPtr); void getArrayShortV (StManArrayFile&, Array* dataPtr); void getArrayuShortV (StManArrayFile&, Array* dataPtr); void getArrayIntV (StManArrayFile&, Array* dataPtr); void getArrayuIntV (StManArrayFile&, Array* dataPtr); void getArrayfloatV (StManArrayFile&, Array* dataPtr); void getArraydoubleV (StManArrayFile&, Array* dataPtr); void getArrayComplexV (StManArrayFile&, Array* dataPtr); void getArrayDComplexV (StManArrayFile&, Array* dataPtr); void getArrayStringV (StManArrayFile&, Array* dataPtr); // // 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 putArrayBoolV (StManArrayFile&, const Array* dataPtr); void putArrayuCharV (StManArrayFile&, const Array* dataPtr); void putArrayShortV (StManArrayFile&, const Array* dataPtr); void putArrayuShortV (StManArrayFile&, const Array* dataPtr); void putArrayIntV (StManArrayFile&, const Array* dataPtr); void putArrayuIntV (StManArrayFile&, const Array* dataPtr); void putArrayfloatV (StManArrayFile&, const Array* dataPtr); void putArraydoubleV (StManArrayFile&, const Array* dataPtr); void putArrayComplexV (StManArrayFile&, const Array* dataPtr); void putArrayDComplexV (StManArrayFile&, const Array* dataPtr); void putArrayStringV (StManArrayFile&, const Array* dataPtr); // // 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 getSliceBoolV (StManArrayFile&, const Slicer&, Array* dataPtr); void getSliceuCharV (StManArrayFile&, const Slicer&, Array* dataPtr); void getSliceShortV (StManArrayFile&, const Slicer&, Array* dataPtr); void getSliceuShortV (StManArrayFile&, const Slicer&, Array* dataPtr); void getSliceIntV (StManArrayFile&, const Slicer&, Array* dataPtr); void getSliceuIntV (StManArrayFile&, const Slicer&, Array* dataPtr); void getSlicefloatV (StManArrayFile&, const Slicer&, Array* dataPtr); void getSlicedoubleV (StManArrayFile&, const Slicer&, Array* dataPtr); void getSliceComplexV (StManArrayFile&, const Slicer&, Array* dataPtr); void getSliceDComplexV (StManArrayFile&, const Slicer&, Array* dataPtr); void getSliceStringV (StManArrayFile&, const Slicer&, Array* dataPtr); // // 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 putSliceBoolV (StManArrayFile&, const Slicer&, const Array* dataPtr); void putSliceuCharV (StManArrayFile&, const Slicer&, const Array* dataPtr); void putSliceShortV (StManArrayFile&, const Slicer&, const Array* dataPtr); void putSliceuShortV (StManArrayFile&, const Slicer&, const Array* dataPtr); void putSliceIntV (StManArrayFile&, const Slicer&, const Array* dataPtr); void putSliceuIntV (StManArrayFile&, const Slicer&, const Array* dataPtr); void putSlicefloatV (StManArrayFile&, const Slicer&, const Array* dataPtr); void putSlicedoubleV (StManArrayFile&, const Slicer&, const Array* dataPtr); void putSliceComplexV (StManArrayFile&, const Slicer&, const Array* dataPtr); void putSliceDComplexV (StManArrayFile&, const Slicer&, const Array* dataPtr); void putSliceStringV (StManArrayFile&, const Slicer&, const Array* dataPtr); // 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, uInt, uInt, uInt, uInt, 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, uInt arrayStart, uInt length, uInt increment, uInt valueIndex, void* value); static void getVecuCharV (StManArrayFile&, Int64 fileOffset, uInt arrayStart, uInt length, uInt increment, uInt valueIndex, void* value); static void getVecShortV (StManArrayFile&, Int64 fileOffset, uInt arrayStart, uInt length, uInt increment, uInt valueIndex, void* value); static void getVecuShortV (StManArrayFile&, Int64 fileOffset, uInt arrayStart, uInt length, uInt increment, uInt valueIndex, void* value); static void getVecIntV (StManArrayFile&, Int64 fileOffset, uInt arrayStart, uInt length, uInt increment, uInt valueIndex, void* value); static void getVecuIntV (StManArrayFile&, Int64 fileOffset, uInt arrayStart, uInt length, uInt increment, uInt valueIndex, void* value); static void getVecfloatV (StManArrayFile&, Int64 fileOffset, uInt arrayStart, uInt length, uInt increment, uInt valueIndex, void* value); static void getVecdoubleV (StManArrayFile&, Int64 fileOffset, uInt arrayStart, uInt length, uInt increment, uInt valueIndex, void* value); static void getVecComplexV (StManArrayFile&, Int64 fileOffset, uInt arrayStart, uInt length, uInt increment, uInt valueIndex, void* value); static void getVecDComplexV (StManArrayFile&, Int64 fileOffset, uInt arrayStart, uInt length, uInt increment, uInt valueIndex, void* value); static void getVecStringV (StManArrayFile&, Int64 fileOffset, uInt arrayStart, uInt length, uInt increment, uInt 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, uInt, uInt, uInt, uInt, 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, uInt arrayStart, uInt length, uInt increment, uInt valueIndex, const void* value); static void putVecuCharV (StManArrayFile&, Int64 fileOffset, uInt arrayStart, uInt length, uInt increment, uInt valueIndex, const void* value); static void putVecShortV (StManArrayFile&, Int64 fileOffset, uInt arrayStart, uInt length, uInt increment, uInt valueIndex, const void* value); static void putVecuShortV (StManArrayFile&, Int64 fileOffset, uInt arrayStart, uInt length, uInt increment, uInt valueIndex, const void* value); static void putVecIntV (StManArrayFile&, Int64 fileOffset, uInt arrayStart, uInt length, uInt increment, uInt valueIndex, const void* value); static void putVecuIntV (StManArrayFile&, Int64 fileOffset, uInt arrayStart, uInt length, uInt increment, uInt valueIndex, const void* value); static void putVecfloatV (StManArrayFile&, Int64 fileOffset, uInt arrayStart, uInt length, uInt increment, uInt valueIndex, const void* value); static void putVecdoubleV (StManArrayFile&, Int64 fileOffset, uInt arrayStart, uInt length, uInt increment, uInt valueIndex, const void* value); static void putVecComplexV (StManArrayFile&, Int64 fileOffset, uInt arrayStart, uInt length, uInt increment, uInt valueIndex, const void* value); static void putVecDComplexV (StManArrayFile&, Int64 fileOffset, uInt arrayStart, uInt length, uInt increment, uInt valueIndex, const void* value); static void putVecStringV (StManArrayFile&, Int64 fileOffset, uInt arrayStart, uInt length, uInt increment, uInt 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-2.4.1/tables/DataMan/StManAipsIO.cc000066400000000000000000000566521321422335000204640ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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) : StManColumn(dataType), stmanPtr_p (smptr), dtype_p (dataType), byPtr_p (byPtr), nralloc_p (0), nrext_p (0), data_p (EXTBLSZ,static_cast(0)), ncum_p (EXTBLSZ,(uInt)0) {} StManColumnAipsIO::~StManColumnAipsIO() { deleteAll(); } void StManColumnAipsIO::doCreate (uInt nrrow) { addRow (nrrow, 0); } void StManColumnAipsIO::reopenRW() {} void StManColumnAipsIO::addRow (uInt nrnew, uInt) { //# Extend the column sizes if needed. if (nrnew > nralloc_p) { uInt n = nralloc_p + 4096; if (n < nrnew) { n = nrnew; } resize (n); } } void StManColumnAipsIO::resize (uInt 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 StManColumnAipsIO::findExt (uInt 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, "StManColumnAipsIO::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; } uInt StManColumnAipsIO::nextExt (void*& ext, uInt& extnr, uInt nrmax) const { if (++extnr > nrext_p) { return 0; } ext = data_p[extnr]; uInt n = ncum_p[extnr]; if (n > nrmax) { n = nrmax; } if (n < ncum_p[extnr-1]) { return 0; } return n - ncum_p[extnr-1]; } #define STMANCOLUMNAIPSIO_GETPUT(T,NM) \ void StManColumnAipsIO::aips_name2(get,NM) (uInt rownr, T* value) \ { \ uInt extnr = findExt(rownr, True); \ *value = ((T*)(data_p[extnr])) [rownr-ncum_p[extnr-1]]; \ } \ void StManColumnAipsIO::aips_name2(put,NM) (uInt rownr, const T* value) \ { \ uInt extnr = findExt(rownr, True); \ ((T*)(data_p[extnr])) [rownr-ncum_p[extnr-1]] = *value; \ stmanPtr_p->setHasPut(); \ } \ uInt StManColumnAipsIO::aips_name2(getBlock,NM) (uInt rownr, uInt nrmax, T* value) \ { \ uInt nr; \ uInt extnr = findExt(rownr, True); \ nrmax = min (nrmax, nralloc_p-rownr); \ uInt nrm = nrmax; \ while (nrmax > 0) { \ nr = min (nrmax, ncum_p[extnr]-rownr); \ objcopy (value, ((T*)(data_p[extnr])) +rownr-ncum_p[extnr-1], nr); \ nrmax -= nr; \ value += nr; \ rownr = ncum_p[extnr]; \ extnr++; \ } \ return nrm; \ } \ void StManColumnAipsIO::aips_name2(putBlock,NM) (uInt rownr, uInt nrmax, const T* value) \ { \ uInt nr; \ uInt extnr = findExt(rownr, True); \ nrmax = min (nrmax, nralloc_p-rownr); \ while (nrmax > 0) { \ nr = min (nrmax, ncum_p[extnr]-rownr); \ objcopy (((T*)(data_p[extnr])) +rownr-ncum_p[extnr-1], value, nr); \ nrmax -= nr; \ value += nr; \ rownr = ncum_p[extnr]; \ extnr++; \ } \ stmanPtr_p->setHasPut(); \ } \ void StManColumnAipsIO::aips_name2(getScalarColumnCells,NM) \ (const RefRows& rownrs, \ Vector* values) \ { \ Bool delV; \ T* value = values->getStorage (delV); \ T* valptr = value; \ const ColumnCache& cache = columnCache(); \ if (rownrs.isSliced()) { \ RefRowsSliceIter iter(rownrs); \ while (! iter.pastEnd()) { \ uInt rownr = iter.sliceStart(); \ uInt end = iter.sliceEnd(); \ uInt incr = iter.sliceIncr(); \ while (rownr <= end) { \ if (rownr < cache.start() || rownr > cache.end()) { \ aips_name2(get,NM) (rownr, valptr); \ } \ uInt inx = rownr - cache.start(); \ const T* cacheValue = (const T*)(cache.dataPtr()) + inx; \ uInt endrow = min (end, cache.end()); \ while (rownr <= endrow) { \ *valptr++ = *cacheValue; \ rownr += incr; \ cacheValue += incr; \ } \ } \ iter++; \ } \ } else { \ const Vector& rowvec = rownrs.rowVector(); \ uInt nr = rowvec.nelements(); \ if (nr > 0) { \ Bool delR; \ const uInt* rows = rowvec.getStorage (delR); \ if (rows[0] < cache.start() || rows[0] > cache.end()) { \ findExt(rows[0], True); \ } \ const T* cacheValue = (const T*)(cache.dataPtr()); \ uInt strow = cache.start(); \ uInt endrow = cache.end(); \ for (uInt i=0; i= strow && rownr <= endrow) { \ value[i] = cacheValue[rownr-strow]; \ } else { \ aips_name2(get,NM) (rownr, &(value[i])); \ cacheValue = (const T*)(cache.dataPtr()); \ strow = cache.start(); \ endrow = cache.end(); \ } \ } \ rowvec.freeStorage (rows, delR); \ } \ } \ values->putStorage (value, delV); \ } STMANCOLUMNAIPSIO_GETPUT(Bool,BoolV) STMANCOLUMNAIPSIO_GETPUT(uChar,uCharV) STMANCOLUMNAIPSIO_GETPUT(Short,ShortV) STMANCOLUMNAIPSIO_GETPUT(uShort,uShortV) STMANCOLUMNAIPSIO_GETPUT(Int,IntV) STMANCOLUMNAIPSIO_GETPUT(uInt,uIntV) STMANCOLUMNAIPSIO_GETPUT(float,floatV) STMANCOLUMNAIPSIO_GETPUT(double,doubleV) STMANCOLUMNAIPSIO_GETPUT(Complex,ComplexV) STMANCOLUMNAIPSIO_GETPUT(DComplex,DComplexV) STMANCOLUMNAIPSIO_GETPUT(String,StringV) void StManColumnAipsIO::remove (uInt index) { //# Find the extension. uInt extnr = findExt(index, False); uInt 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 StManColumnAipsIO::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 StManColumnAipsIO::deleteData (void* datap, Bool byPtr) { if (byPtr) { delete [] (void**)datap; }else{ switch (dtype_p) { case TpBool: delete [] (Bool*)datap; break; case TpUChar: delete [] (uChar*)datap; break; case TpShort: delete [] (Short*)datap; break; case TpUShort: delete [] (uShort*)datap; break; case TpInt: delete [] (Int*)datap; break; case TpUInt: delete [] (uInt*)datap; break; case TpFloat: delete [] (float*)datap; break; case TpDouble: delete [] (double*)datap; break; case TpComplex: delete [] (Complex*)datap; break; case TpDComplex: delete [] (DComplex*)datap; break; case TpString: delete [] (String*)datap; break; default: throw DataManInvDT(); } } datap = 0; } void* StManColumnAipsIO::allocData (uInt nrval, Bool byPtr) { void* datap = 0; if (byPtr) { datap = new void*[nrval]; if (datap != 0) { void** dp = (void**)datap; for (uInt i=0; i= nrvalAfter) { return; } if (byPtr_p) { objmove (((void**)dp) + inx, ((void**)dp) + inx+1 ,nrvalAfter-inx); return; } switch (dtype_p) { case TpBool: objmove (((Bool*)dp) + inx, ((Bool*)dp) + inx+1, nrvalAfter-inx); break; case TpUChar: objmove (((uChar*)dp) + inx, ((uChar*)dp) + inx+1, nrvalAfter-inx); break; case TpShort: objmove (((Short*)dp) + inx, ((Short*)dp) + inx+1, nrvalAfter-inx); break; case TpUShort: objmove (((uShort*)dp) + inx, ((uShort*)dp) + inx+1,nrvalAfter-inx); break; case TpInt: objmove (((Int*)dp) + inx, ((Int*)dp) + inx+1, nrvalAfter-inx); break; case TpUInt: objmove (((uInt*)dp) + inx, ((uInt*)dp) + inx+1, nrvalAfter-inx); break; case TpFloat: objmove (((float*)dp) + inx, ((float*)dp) + inx+1, nrvalAfter-inx); break; case TpDouble: objmove (((double*)dp) + inx, ((double*)dp) + inx+1,nrvalAfter-inx); break; case TpComplex: objmove (((Complex*)dp)+inx, ((Complex*)dp)+inx+1, nrvalAfter-inx); break; case TpDComplex: objmove (((DComplex*)dp)+inx, ((DComplex*)dp)+inx+1,nrvalAfter-inx); break; case TpString: objmove (((String*)dp) + inx, ((String*)dp) + inx+1,nrvalAfter-inx); break; default: throw DataManInvDT(); } } //# Write all data into AipsIO. void StManColumnAipsIO::putFile (uInt nrval, AipsIO& ios) { ios.putstart ("StManColumnAipsIO", 2); // class version 2 ios << 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_p) { 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 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; } } //# Read all data from AipsIO. void StManColumnAipsIO::getFile (uInt 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_p) { 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 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; } } void* StManColumnAipsIO::getArrayPtr (uInt rownr) { uInt extnr = findExt(rownr, False); return ((void**)(data_p[extnr])) [rownr-ncum_p[extnr-1]]; } void StManColumnAipsIO::putArrayPtr (uInt rownr, void* ptr) { uInt extnr = findExt(rownr, False); ((void**)(data_p[extnr])) [rownr-ncum_p[extnr-1]] = ptr; stmanPtr_p->setHasPut(); } StManAipsIO::StManAipsIO () : DataManager (), uniqnr_p (0), nrrow_p (0), colSet_p (0), hasPut_p (False), iosfile_p (0) {} StManAipsIO::StManAipsIO (const String& storageManagerName) : DataManager (), stmanName_p (storageManagerName), uniqnr_p (0), nrrow_p (0), colSet_p (0), hasPut_p (False), iosfile_p (0) {} StManAipsIO::StManAipsIO (const String& storageManagerName, const Record&) : DataManager (), stmanName_p (storageManagerName), uniqnr_p (0), nrrow_p (0), colSet_p (0), hasPut_p (False), iosfile_p (0) {} StManAipsIO::~StManAipsIO() { for (uInt i=0; i= 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; } // Note that the column has already been added by makeXXColumn. // This function is merely for initializing the added column. void StManAipsIO::addColumn (DataManagerColumn* colp) { for (uInt i=0; idoCreate (nrrow_p); setHasPut(); return; } } throw DataManInternalError ("StManAipsIO::addColumn"); } void StManAipsIO::removeColumn (DataManagerColumn* colp) { for (uInt i=0; icolumnName() + " does not exist"); } void StManAipsIO::addRow (uInt nr) { //# Add the number of rows to each column. for (uInt i=0; iaddRow (nrrow_p+nr, nrrow_p); } nrrow_p += nr; setHasPut(); } void StManAipsIO::removeRow (uInt rownr) { for (uInt i=0; iremove (rownr); } nrrow_p--; setHasPut(); } 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. ios << stmanName_p; // this is added in version 2 ios << sequenceNr(); ios << uniqnr_p; ios << nrrow_p; ios << ncolumn(); for (i=0; idataType(); } for (i=0; iputFile (nrrow_p, ios); } ios.putend(); hasPut_p = False; return True; } void StManAipsIO::create (uInt nrrow) { nrrow_p = nrrow; //# Let the column create something if needed. for (uInt i=0; idoCreate (nrrow); } setHasPut(); } void StManAipsIO::open (uInt tabNrrow, AipsIO&) { resync (tabNrrow); } void StManAipsIO::resync (uInt 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, nrc, snr; int dt; if (version > 1) { ios >> stmanName_p; } ios >> snr; ios >> uniqnr_p; ios >> nrrow_p; ios >> nrc; if (snr != sequenceNr() || nrc != ncolumn()) { throw DataManInternalError ("StManAipsIO::open: mismatch in seqnr,#col"); } if (nrrow != nrrow_p) { #if defined(TABLEREPAIR) cerr << "StManAipsIO::open: mismatch in #row (expected " << nrrow << ", found " << nrrow_p << ")" << 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(nrrow_p)); #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 (nrrow_p, 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 > nrrow_p) { colSet_p[i]->addRow (nrrow, nrrow_p); } else if (nrrow < nrrow_p) { for (uInt r=nrrow; rremove (nrrow); } } } nrrow_p = nrrow; ios.getend(); } 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-2.4.1/tables/DataMan/StManAipsIO.h000066400000000000000000000446231321422335000203210ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef TABLES_STMANAIPSIO_H #define TABLES_STMANAIPSIO_H //# Includes #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. //
      • StManColumn // // // 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 StManColumn { 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(); // 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). // void getBoolV (uInt rownr, Bool* dataPtr); void getuCharV (uInt rownr, uChar* dataPtr); void getShortV (uInt rownr, Short* dataPtr); void getuShortV (uInt rownr, uShort* dataPtr); void getIntV (uInt rownr, Int* dataPtr); void getuIntV (uInt rownr, uInt* dataPtr); void getfloatV (uInt rownr, float* dataPtr); void getdoubleV (uInt rownr, double* dataPtr); void getComplexV (uInt rownr, Complex* dataPtr); void getDComplexV (uInt rownr, DComplex* dataPtr); void getStringV (uInt 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). // void putBoolV (uInt rownr, const Bool* dataPtr); void putuCharV (uInt rownr, const uChar* dataPtr); void putShortV (uInt rownr, const Short* dataPtr); void putuShortV (uInt rownr, const uShort* dataPtr); void putIntV (uInt rownr, const Int* dataPtr); void putuIntV (uInt rownr, const uInt* dataPtr); void putfloatV (uInt rownr, const float* dataPtr); void putdoubleV (uInt rownr, const double* dataPtr); void putComplexV (uInt rownr, const Complex* dataPtr); void putDComplexV (uInt rownr, const DComplex* dataPtr); void putStringV (uInt rownr, const String* dataPtr); // // Get scalars from the given row on with a maximum of nrmax values. // This can be used to get an entire column of scalars or to get // a part of a column (for a cache for example). // The buffer pointed to by dataPtr has to have the correct length // (which is guaranteed by the ScalarColumn get function). // uInt getBlockBoolV (uInt rownr, uInt nrmax, Bool* dataPtr); uInt getBlockuCharV (uInt rownr, uInt nrmax, uChar* dataPtr); uInt getBlockShortV (uInt rownr, uInt nrmax, Short* dataPtr); uInt getBlockuShortV (uInt rownr, uInt nrmax, uShort* dataPtr); uInt getBlockIntV (uInt rownr, uInt nrmax, Int* dataPtr); uInt getBlockuIntV (uInt rownr, uInt nrmax, uInt* dataPtr); uInt getBlockfloatV (uInt rownr, uInt nrmax, float* dataPtr); uInt getBlockdoubleV (uInt rownr, uInt nrmax, double* dataPtr); uInt getBlockComplexV (uInt rownr, uInt nrmax, Complex* dataPtr); uInt getBlockDComplexV (uInt rownr, uInt nrmax, DComplex* dataPtr); uInt getBlockStringV (uInt rownr, uInt nrmax, String* dataPtr); // // Put nrmax scalars from the given row on. // This can be used to put an entire column of scalars or to put // a part of a column (for a cache for example). // The buffer pointed to by dataPtr has to have the correct length // (which is guaranteed by the ScalarColumn put function). // void putBlockBoolV (uInt rownr, uInt nrmax, const Bool* dataPtr); void putBlockuCharV (uInt rownr, uInt nrmax, const uChar* dataPtr); void putBlockShortV (uInt rownr, uInt nrmax, const Short* dataPtr); void putBlockuShortV (uInt rownr, uInt nrmax, const uShort* dataPtr); void putBlockIntV (uInt rownr, uInt nrmax, const Int* dataPtr); void putBlockuIntV (uInt rownr, uInt nrmax, const uInt* dataPtr); void putBlockfloatV (uInt rownr, uInt nrmax, const float* dataPtr); void putBlockdoubleV (uInt rownr, uInt nrmax, const double* dataPtr); void putBlockComplexV (uInt rownr, uInt nrmax, const Complex* dataPtr); void putBlockDComplexV (uInt rownr, uInt nrmax, const DComplex* dataPtr); void putBlockStringV (uInt rownr, uInt nrmax, const String* 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 implementation loops through all rows. // 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 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); // // Add (newNrrow-oldNrrow) rows to the column. virtual void addRow (uInt newNrrow, uInt oldNrrow); // Resize the data blocks. // This adds an extension when needed. void resize (uInt nrval); // Remove the given row. // If no rows remain in the extension, the extension is also removed. virtual void remove (uInt rownr); // Create the number of rows in a new table. // This is used when a table gets created. virtual void doCreate (uInt nrrow); // Write the column data into AipsIO. // It will successively write all extensions using putData. virtual void putFile (uInt nrval, AipsIO&); // Read the column data from AipsIO. // One extension gets allocated to hold all rows in the column. virtual void getFile (uInt 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: // The storage manager. StManAipsIO* stmanPtr_p; // The data type (for caching purposes). int dtype_p; // The data is indirectly accessed via a pointer (for the derived classes). Bool byPtr_p; // The number of allocated rows in the column. uInt 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 (uInt rownr, Bool setCache); // Get the next extension. // For the first iteration extnr should be zero. // It returns the number of values in it until the maximum is reached. // Zero means no more extensions. uInt nextExt (void*& ext, uInt& extnr, uInt nrmax) const; // Allocate an extension with the data type of the column. void* allocData (uInt nrval, Bool byPtr); // Delete all extensions. // Possible underlying data (as used by StManArrayColumnAipsIO) // 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, uInt inx, uInt nrvalAfter); // 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); // Get the pointer for the given row. // This is for the derived classes like StManArrayColumnAipsIO. void* getArrayPtr (uInt rownr); // Put the pointer for the given row. // This is for the derived classes like StManArrayColumnAipsIO. void putArrayPtr (uInt rownr, void* dataPtr); private: // Forbid copy constructor. StManColumnAipsIO (const StManColumnAipsIO&); // Forbid assignment. StManColumnAipsIO& operator= (const StManColumnAipsIO&); }; // // 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 DataManager { 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&); // ~StManAipsIO(); // Clone this object. // It does not clone StManAipsIOColumn objects possibly used. DataManager* clone() const; // Get the type name of the data manager (i.e. StManAipsIO). String dataManagerType() const; // Get the name given to this storage manager. String dataManagerName() 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++; } // Get the nr of rows in this storage manager. uInt nrow() const { return nrrow_p; } // Set the hasPut_p flag. In this way the StManAipsIOColumn objects // can indicate that data have been put. void setHasPut() { hasPut_p = True; } // Does the storage manager allow to add rows? (yes) Bool canAddRow() const; // Does the storage manager allow to delete rows? (yes) Bool canRemoveRow() const; // Does the storage manager allow to add columns? (yes) Bool canAddColumn() const; // Does the storage manager allow to delete columns? (yes) 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); // Open (if needed) the file for indirect arrays with the given mode. // Return a pointer to the object. StManArrayFile* openArrayFile (ByteIO::OpenOption opt); private: // Forbid copy constructor. StManAipsIO (const StManAipsIO&); // Forbid assignment. StManAipsIO& operator= (const StManAipsIO&); // 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 create (uInt nrrow); // Open the storage manager file for an existing table and read in // the data and let the StManColumnAipsIO objects read their data. virtual void open (uInt nrrow, AipsIO&); // Resync the storage manager with the new file contents. // This is done by clearing the cache. virtual void resync (uInt 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(); // Add rows to all columns. void addRow (uInt nrrow); // Delete a row from all columns. void removeRow (uInt rownr); // 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); // // Add a column. void addColumn (DataManagerColumn*); // Delete a column. void removeColumn (DataManagerColumn*); // Name given by user to this storage manager. String stmanName_p; // Unique nr for column in this storage manager. uInt uniqnr_p; // The number of rows in the columns. uInt nrrow_p; // The assembly of all columns. PtrBlock colSet_p; // Has anything been put since the last flush? Bool hasPut_p; // The file containing the indirect arrays. StManArrayFile* iosfile_p; }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/tables/DataMan/StManColumn.cc000066400000000000000000000757011321422335000205710ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# Includes #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN StManColumn::StManColumn (int dataType) : dtype_p (dataType) {} StManColumn::~StManColumn() {} int StManColumn::dataType() const { return dtype_p; } //# Accessing an entire column is by default possible for scalars. Bool StManColumn::canAccessScalarColumn (Bool& reask) const { reask = False; return True; } //# Accessing column cells is possible for scalars and arrays. Bool StManColumn::canAccessScalarColumnCells (Bool& reask) const { reask = False; return True; } Bool StManColumn::canAccessArrayColumnCells (Bool& reask) const { reask = False; return True; } Bool StManColumn::isNativeDataType (int dtype) { switch (dtype) { case TpBool: case TpUChar: case TpShort: case TpUShort: case TpInt: case TpUInt: case TpFloat: case TpDouble: case TpComplex: case TpDComplex: case TpString: case TpArrayBool: case TpArrayUChar: case TpArrayShort: case TpArrayUShort: case TpArrayInt: case TpArrayUInt: case TpArrayFloat: case TpArrayDouble: case TpArrayComplex: case TpArrayDComplex: case TpArrayString: return True; } return False; } //# Call the correct getScalarColumnX function depending on the data type. void StManColumn::getScalarColumnV (void* dataPtr) { switch (dtype_p) { case TpBool: getScalarColumnBoolV ((Vector*)dataPtr); break; case TpUChar: getScalarColumnuCharV ((Vector*)dataPtr); break; case TpShort: getScalarColumnShortV ((Vector*)dataPtr); break; case TpUShort: getScalarColumnuShortV ((Vector*)dataPtr); break; case TpInt: getScalarColumnIntV ((Vector*)dataPtr); break; case TpUInt: getScalarColumnuIntV ((Vector*)dataPtr); break; case TpFloat: getScalarColumnfloatV ((Vector*)dataPtr); break; case TpDouble: getScalarColumndoubleV ((Vector*)dataPtr); break; case TpComplex: getScalarColumnComplexV ((Vector*)dataPtr); break; case TpDComplex: getScalarColumnDComplexV ((Vector*)dataPtr); break; case TpString: getScalarColumnStringV ((Vector*)dataPtr); break; default: throw (DataManInvDT ("StManColumn::getScalarColumn")); } } //# Call the correct putScalarColumnX function depending on the data type. void StManColumn::putScalarColumnV (const void* dataPtr) { switch (dtype_p) { case TpBool: putScalarColumnBoolV ((const Vector*)dataPtr); break; case TpUChar: putScalarColumnuCharV ((const Vector*)dataPtr); break; case TpShort: putScalarColumnShortV ((const Vector*)dataPtr); break; case TpUShort: putScalarColumnuShortV ((const Vector*)dataPtr); break; case TpInt: putScalarColumnIntV ((const Vector*)dataPtr); break; case TpUInt: putScalarColumnuIntV ((const Vector*)dataPtr); break; case TpFloat: putScalarColumnfloatV ((const Vector*)dataPtr); break; case TpDouble: putScalarColumndoubleV ((const Vector*)dataPtr); break; case TpComplex: putScalarColumnComplexV ((const Vector*)dataPtr); break; case TpDComplex: putScalarColumnDComplexV ((const Vector*)dataPtr); break; case TpString: putScalarColumnStringV ((const Vector*)dataPtr); break; default: throw (DataManInvDT ("StManColumn::putScalarColumn")); } } //# Call the correct getScalarColumnCellsX function depending on the data type. void StManColumn::getScalarColumnCellsV (const RefRows& rownrs, void* dataPtr) { switch (dtype_p) { case TpBool: getScalarColumnCellsBoolV (rownrs, (Vector*)dataPtr); break; case TpUChar: getScalarColumnCellsuCharV (rownrs, (Vector*)dataPtr); break; case TpShort: getScalarColumnCellsShortV (rownrs, (Vector*)dataPtr); break; case TpUShort: getScalarColumnCellsuShortV (rownrs, (Vector*)dataPtr); break; case TpInt: getScalarColumnCellsIntV (rownrs, (Vector*)dataPtr); break; case TpUInt: getScalarColumnCellsuIntV (rownrs, (Vector*)dataPtr); break; case TpFloat: getScalarColumnCellsfloatV (rownrs, (Vector*)dataPtr); break; case TpDouble: getScalarColumnCellsdoubleV (rownrs, (Vector*)dataPtr); break; case TpComplex: getScalarColumnCellsComplexV (rownrs, (Vector*)dataPtr); break; case TpDComplex: getScalarColumnCellsDComplexV (rownrs, (Vector*)dataPtr); break; case TpString: getScalarColumnCellsStringV (rownrs, (Vector*)dataPtr); break; default: throw (DataManInvDT ("StManColumn::getScalarColumnCells")); } } //# Call the correct putScalarColumnCellsX function depending on the data type. void StManColumn::putScalarColumnCellsV (const RefRows& rownrs, const void* dataPtr) { switch (dtype_p) { case TpBool: putScalarColumnCellsBoolV (rownrs, (const Vector*)dataPtr); break; case TpUChar: putScalarColumnCellsuCharV (rownrs, (const Vector*)dataPtr); break; case TpShort: putScalarColumnCellsShortV (rownrs, (const Vector*)dataPtr); break; case TpUShort: putScalarColumnCellsuShortV (rownrs, (const Vector*)dataPtr); break; case TpInt: putScalarColumnCellsIntV (rownrs, (const Vector*)dataPtr); break; case TpUInt: putScalarColumnCellsuIntV (rownrs, (const Vector*)dataPtr); break; case TpFloat: putScalarColumnCellsfloatV (rownrs, (const Vector*)dataPtr); break; case TpDouble: putScalarColumnCellsdoubleV (rownrs, (const Vector*)dataPtr); break; case TpComplex: putScalarColumnCellsComplexV (rownrs, (const Vector*)dataPtr); break; case TpDComplex: putScalarColumnCellsDComplexV (rownrs, (const Vector*)dataPtr); break; case TpString: putScalarColumnCellsStringV (rownrs, (const Vector*)dataPtr); break; default: throw (DataManInvDT ("StManColumn::putScalarColumnCells")); } } //# Call the correct getBlockX function depending on the data type. uInt StManColumn::getBlockV (uInt rownr, uInt nrmax, void* dataPtr) { switch (dtype_p) { case TpBool: return getBlockBoolV (rownr, nrmax, (Bool*)dataPtr); case TpUChar: return getBlockuCharV (rownr, nrmax, (uChar*)dataPtr); case TpShort: return getBlockShortV (rownr, nrmax, (Short*)dataPtr); case TpUShort: return getBlockuShortV (rownr, nrmax, (uShort*)dataPtr); case TpInt: return getBlockIntV (rownr, nrmax, (Int*)dataPtr); case TpUInt: return getBlockuIntV (rownr, nrmax, (uInt*)dataPtr); case TpFloat: return getBlockfloatV (rownr, nrmax, (float*)dataPtr); case TpDouble: return getBlockdoubleV (rownr, nrmax, (double*)dataPtr); case TpComplex: return getBlockComplexV (rownr, nrmax, (Complex*)dataPtr); case TpDComplex: return getBlockDComplexV (rownr, nrmax, (DComplex*)dataPtr); case TpString: return getBlockStringV (rownr, nrmax, (String*)dataPtr); default: throw (DataManInvDT ("StManColumn::getBlock")); } // NOTREACHED - to shut up a warning message return 0; } //# Call the correct putBlockX function depending on the data type. void StManColumn::putBlockV (uInt rownr, uInt nrmax, const void* dataPtr) { switch (dtype_p) { case TpBool: putBlockBoolV (rownr, nrmax, (const Bool*)dataPtr); break; case TpUChar: putBlockuCharV (rownr, nrmax, (const uChar*)dataPtr); break; case TpShort: putBlockShortV (rownr, nrmax, (const Short*)dataPtr); break; case TpUShort: putBlockuShortV (rownr, nrmax, (const uShort*)dataPtr); break; case TpInt: putBlockIntV (rownr, nrmax, (const Int*)dataPtr); break; case TpUInt: putBlockuIntV (rownr, nrmax, (const uInt*)dataPtr); break; case TpFloat: putBlockfloatV (rownr, nrmax, (const float*)dataPtr); break; case TpDouble: putBlockdoubleV (rownr, nrmax, (const double*)dataPtr); break; case TpComplex: putBlockComplexV (rownr, nrmax, (const Complex*)dataPtr); break; case TpDComplex: putBlockDComplexV (rownr, nrmax, (const DComplex*)dataPtr); break; case TpString: putBlockStringV (rownr, nrmax, (const String*)dataPtr); break; default: throw (DataManInvDT ("StManColumn::putBlock")); } } //# Call the correct getArrayX function depending on the data type. void StManColumn::getArrayV (uInt rownr, void* dataPtr) { switch (dtype_p) { case TpBool: getArrayBoolV (rownr, (Array*)dataPtr); break; case TpUChar: getArrayuCharV (rownr, (Array*)dataPtr); break; case TpShort: getArrayShortV (rownr, (Array*)dataPtr); break; case TpUShort: getArrayuShortV (rownr, (Array*)dataPtr); break; case TpInt: getArrayIntV (rownr, (Array*)dataPtr); break; case TpUInt: getArrayuIntV (rownr, (Array*)dataPtr); break; case TpFloat: getArrayfloatV (rownr, (Array*)dataPtr); break; case TpDouble: getArraydoubleV (rownr, (Array*)dataPtr); break; case TpComplex: getArrayComplexV (rownr, (Array*)dataPtr); break; case TpDComplex: getArrayDComplexV (rownr, (Array*)dataPtr); break; case TpString: getArrayStringV (rownr, (Array*)dataPtr); break; default: throw (DataManInvDT ("StManColumn::getArray")); } } //# Call the correct putArrayX function depending on the data type. void StManColumn::putArrayV (uInt rownr, const void* dataPtr) { switch (dtype_p) { case TpBool: putArrayBoolV (rownr, (const Array*)dataPtr); break; case TpUChar: putArrayuCharV (rownr, (const Array*)dataPtr); break; case TpShort: putArrayShortV (rownr, (const Array*)dataPtr); break; case TpUShort: putArrayuShortV (rownr, (const Array*)dataPtr); break; case TpInt: putArrayIntV (rownr, (const Array*)dataPtr); break; case TpUInt: putArrayuIntV (rownr, (const Array*)dataPtr); break; case TpFloat: putArrayfloatV (rownr, (const Array*)dataPtr); break; case TpDouble: putArraydoubleV (rownr, (const Array*)dataPtr); break; case TpComplex: putArrayComplexV (rownr, (const Array*)dataPtr); break; case TpDComplex: putArrayDComplexV (rownr, (const Array*)dataPtr); break; case TpString: putArrayStringV (rownr, (const Array*)dataPtr); break; default: throw (DataManInvDT ("StManColumn::putArray")); } } //# Call the correct getColumnSliceX function depending on the data type. void StManColumn::getSliceV (uInt rownr, const Slicer& ns, void* dataPtr) { switch (dtype_p) { case TpBool: getSliceBoolV (rownr, ns, (Array*)dataPtr); break; case TpUChar: getSliceuCharV (rownr, ns, (Array*)dataPtr); break; case TpShort: getSliceShortV (rownr, ns, (Array*)dataPtr); break; case TpUShort: getSliceuShortV (rownr, ns, (Array*)dataPtr); break; case TpInt: getSliceIntV (rownr, ns, (Array*)dataPtr); break; case TpUInt: getSliceuIntV (rownr, ns, (Array*)dataPtr); break; case TpFloat: getSlicefloatV (rownr, ns, (Array*)dataPtr); break; case TpDouble: getSlicedoubleV (rownr, ns, (Array*)dataPtr); break; case TpComplex: getSliceComplexV (rownr, ns, (Array*)dataPtr); break; case TpDComplex: getSliceDComplexV (rownr, ns, (Array*)dataPtr); break; case TpString: getSliceStringV (rownr, ns, (Array*)dataPtr); break; default: throw (DataManInvDT ("StManColumn::getSlice")); } } //# Call the correct putSliceX function depending on the data type. void StManColumn::putSliceV (uInt rownr, const Slicer& ns, const void* dataPtr) { switch (dtype_p) { case TpBool: putSliceBoolV (rownr, ns, (const Array*)dataPtr); break; case TpUChar: putSliceuCharV (rownr, ns, (const Array*)dataPtr); break; case TpShort: putSliceShortV (rownr, ns, (const Array*)dataPtr); break; case TpUShort: putSliceuShortV (rownr, ns, (const Array*)dataPtr); break; case TpInt: putSliceIntV (rownr, ns, (const Array*)dataPtr); break; case TpUInt: putSliceuIntV (rownr, ns, (const Array*)dataPtr); break; case TpFloat: putSlicefloatV (rownr, ns, (const Array*)dataPtr); break; case TpDouble: putSlicedoubleV (rownr, ns, (const Array*)dataPtr); break; case TpComplex: putSliceComplexV (rownr, ns, (const Array*)dataPtr); break; case TpDComplex: putSliceDComplexV (rownr, ns, (const Array*)dataPtr); break; case TpString: putSliceStringV (rownr, ns, (const Array*)dataPtr); break; default: throw (DataManInvDT ("StManColumn::putSlice")); } } //# Call the correct getArrayColumnX function depending on the data type. void StManColumn::getArrayColumnV (void* dataPtr) { switch (dtype_p) { case TpBool: getArrayColumnBoolV ((Array*)dataPtr); break; case TpUChar: getArrayColumnuCharV ((Array*)dataPtr); break; case TpShort: getArrayColumnShortV ((Array*)dataPtr); break; case TpUShort: getArrayColumnuShortV ((Array*)dataPtr); break; case TpInt: getArrayColumnIntV ((Array*)dataPtr); break; case TpUInt: getArrayColumnuIntV ((Array*)dataPtr); break; case TpFloat: getArrayColumnfloatV ((Array*)dataPtr); break; case TpDouble: getArrayColumndoubleV ((Array*)dataPtr); break; case TpComplex: getArrayColumnComplexV ((Array*)dataPtr); break; case TpDComplex: getArrayColumnDComplexV ((Array*)dataPtr); break; case TpString: getArrayColumnStringV ((Array*)dataPtr); break; default: throw (DataManInvDT ("StManColumn::getArrayColumn")); } } //# Call the correct putArrayColumnX function depending on the data type. void StManColumn::putArrayColumnV (const void* dataPtr) { switch (dtype_p) { case TpBool: putArrayColumnBoolV ((const Array*)dataPtr); break; case TpUChar: putArrayColumnuCharV ((const Array*)dataPtr); break; case TpShort: putArrayColumnShortV ((const Array*)dataPtr); break; case TpUShort: putArrayColumnuShortV ((const Array*)dataPtr); break; case TpInt: putArrayColumnIntV ((const Array*)dataPtr); break; case TpUInt: putArrayColumnuIntV ((const Array*)dataPtr); break; case TpFloat: putArrayColumnfloatV ((const Array*)dataPtr); break; case TpDouble: putArrayColumndoubleV ((const Array*)dataPtr); break; case TpComplex: putArrayColumnComplexV ((const Array*)dataPtr); break; case TpDComplex: putArrayColumnDComplexV ((const Array*)dataPtr); break; case TpString: putArrayColumnStringV ((const Array*)dataPtr); break; default: throw (DataManInvDT ("StManColumn::putArrayColumn")); } } //# Call the correct getArrayColumnCellsX function depending on the data type. void StManColumn::getArrayColumnCellsV (const RefRows& rownrs, void* dataPtr) { switch (dtype_p) { case TpBool: getArrayColumnCellsBoolV (rownrs, (Array*)dataPtr); break; case TpUChar: getArrayColumnCellsuCharV (rownrs, (Array*)dataPtr); break; case TpShort: getArrayColumnCellsShortV (rownrs, (Array*)dataPtr); break; case TpUShort: getArrayColumnCellsuShortV (rownrs, (Array*)dataPtr); break; case TpInt: getArrayColumnCellsIntV (rownrs, (Array*)dataPtr); break; case TpUInt: getArrayColumnCellsuIntV (rownrs, (Array*)dataPtr); break; case TpFloat: getArrayColumnCellsfloatV (rownrs, (Array*)dataPtr); break; case TpDouble: getArrayColumnCellsdoubleV (rownrs, (Array*)dataPtr); break; case TpComplex: getArrayColumnCellsComplexV (rownrs, (Array*)dataPtr); break; case TpDComplex: getArrayColumnCellsDComplexV (rownrs, (Array*)dataPtr); break; case TpString: getArrayColumnCellsStringV (rownrs, (Array*)dataPtr); break; default: throw (DataManInvDT ("StManColumn::getArrayColumnCells")); } } //# Call the correct putArrayColumnCellsX function depending on the data type. void StManColumn::putArrayColumnCellsV (const RefRows& rownrs, const void* dataPtr) { switch (dtype_p) { case TpBool: putArrayColumnCellsBoolV (rownrs, (const Array*)dataPtr); break; case TpUChar: putArrayColumnCellsuCharV (rownrs, (const Array*)dataPtr); break; case TpShort: putArrayColumnCellsShortV (rownrs, (const Array*)dataPtr); break; case TpUShort: putArrayColumnCellsuShortV (rownrs, (const Array*)dataPtr); break; case TpInt: putArrayColumnCellsIntV (rownrs, (const Array*)dataPtr); break; case TpUInt: putArrayColumnCellsuIntV (rownrs, (const Array*)dataPtr); break; case TpFloat: putArrayColumnCellsfloatV (rownrs, (const Array*)dataPtr); break; case TpDouble: putArrayColumnCellsdoubleV (rownrs, (const Array*)dataPtr); break; case TpComplex: putArrayColumnCellsComplexV (rownrs, (const Array*)dataPtr); break; case TpDComplex: putArrayColumnCellsDComplexV (rownrs, (const Array*)dataPtr); break; case TpString: putArrayColumnCellsStringV (rownrs, (const Array*)dataPtr); break; default: throw (DataManInvDT ("StManColumn::putArrayColumnCells")); } } //# Call the correct getColumnSliceX function depending on the data type. void StManColumn::getColumnSliceV (const Slicer& ns, void* dataPtr) { switch (dtype_p) { case TpBool: getColumnSliceBoolV (ns, (Array*)dataPtr); break; case TpUChar: getColumnSliceuCharV (ns, (Array*)dataPtr); break; case TpShort: getColumnSliceShortV (ns, (Array*)dataPtr); break; case TpUShort: getColumnSliceuShortV (ns, (Array*)dataPtr); break; case TpInt: getColumnSliceIntV (ns, (Array*)dataPtr); break; case TpUInt: getColumnSliceuIntV (ns, (Array*)dataPtr); break; case TpFloat: getColumnSlicefloatV (ns, (Array*)dataPtr); break; case TpDouble: getColumnSlicedoubleV (ns, (Array*)dataPtr); break; case TpComplex: getColumnSliceComplexV (ns, (Array*)dataPtr); break; case TpDComplex: getColumnSliceDComplexV (ns, (Array*)dataPtr); break; case TpString: getColumnSliceStringV (ns, (Array*)dataPtr); break; default: throw (DataManInvDT ("StManColumn::getColumnSlice")); } } //# Call the correct putColumnSliceX function depending on the data type. void StManColumn::putColumnSliceV (const Slicer& ns, const void* dataPtr) { switch (dtype_p) { case TpBool: putColumnSliceBoolV (ns, (const Array*)dataPtr); break; case TpUChar: putColumnSliceuCharV (ns, (const Array*)dataPtr); break; case TpShort: putColumnSliceShortV (ns, (const Array*)dataPtr); break; case TpUShort: putColumnSliceuShortV (ns, (const Array*)dataPtr); break; case TpInt: putColumnSliceIntV (ns, (const Array*)dataPtr); break; case TpUInt: putColumnSliceuIntV (ns, (const Array*)dataPtr); break; case TpFloat: putColumnSlicefloatV (ns, (const Array*)dataPtr); break; case TpDouble: putColumnSlicedoubleV (ns, (const Array*)dataPtr); break; case TpComplex: putColumnSliceComplexV (ns, (const Array*)dataPtr); break; case TpDComplex: putColumnSliceDComplexV (ns, (const Array*)dataPtr); break; case TpString: putColumnSliceStringV (ns, (const Array*)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, void* dataPtr) { switch (dtype_p) { case TpBool: getColumnSliceCellsBoolV (rownrs, ns, (Array*)dataPtr); break; case TpUChar: getColumnSliceCellsuCharV (rownrs, ns, (Array*)dataPtr); break; case TpShort: getColumnSliceCellsShortV (rownrs, ns, (Array*)dataPtr); break; case TpUShort: getColumnSliceCellsuShortV (rownrs, ns, (Array*)dataPtr); break; case TpInt: getColumnSliceCellsIntV (rownrs, ns, (Array*)dataPtr); break; case TpUInt: getColumnSliceCellsuIntV (rownrs, ns, (Array*)dataPtr); break; case TpFloat: getColumnSliceCellsfloatV (rownrs, ns, (Array*)dataPtr); break; case TpDouble: getColumnSliceCellsdoubleV (rownrs, ns, (Array*)dataPtr); break; case TpComplex: getColumnSliceCellsComplexV (rownrs, ns, (Array*)dataPtr); break; case TpDComplex: getColumnSliceCellsDComplexV (rownrs, ns, (Array*)dataPtr); break; case TpString: getColumnSliceCellsStringV (rownrs, ns, (Array*)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 void* dataPtr) { switch (dtype_p) { case TpBool: putColumnSliceCellsBoolV (rownrs, ns, (const Array*)dataPtr); break; case TpUChar: putColumnSliceCellsuCharV (rownrs, ns, (const Array*)dataPtr); break; case TpShort: putColumnSliceCellsShortV (rownrs, ns, (const Array*)dataPtr); break; case TpUShort: putColumnSliceCellsuShortV (rownrs, ns, (const Array*)dataPtr); break; case TpInt: putColumnSliceCellsIntV (rownrs, ns, (const Array*)dataPtr); break; case TpUInt: putColumnSliceCellsuIntV (rownrs, ns, (const Array*)dataPtr); break; case TpFloat: putColumnSliceCellsfloatV (rownrs, ns, (const Array*)dataPtr); break; case TpDouble: putColumnSliceCellsdoubleV (rownrs, ns, (const Array*)dataPtr); break; case TpComplex: putColumnSliceCellsComplexV (rownrs, ns, (const Array*)dataPtr); break; case TpDComplex: putColumnSliceCellsDComplexV (rownrs, ns, (const Array*)dataPtr); break; case TpString: putColumnSliceCellsStringV (rownrs, ns, (const Array*)dataPtr); break; default: throw (DataManInvDT ("StManColumn::putColumnSliceCells")); } } void StManColumn::throwGetArray() const { throw (DataManInvOper ("StManColumn::getArray not possible" " for column " + columnName())); } void StManColumn::throwPutArray() const { throw (DataManInvOper ("StManColumn::putArray not possible" " for column " + columnName())); } //# For scalars the default implementation of get/putScalarColumn handles //# its data using get/putBlock. For arrays it throws an exception. //# The default implementation of getBlock gets one value. //# The default implementation of putBlock puts one value at a time. #define STMANCOLUMN_GETPUT(T,NM) \ void StManColumn::aips_name2(getScalarColumn,NM) (Vector* dataPtr) \ { \ Bool deleteIt; \ T* data = dataPtr->getStorage (deleteIt); \ uInt nrdone = 0; \ uInt nrgot; \ for (uInt nrtodo=dataPtr->nelements(); nrtodo>0;) { \ nrgot = aips_name2(getBlock,NM) (nrdone, nrtodo, data); \ nrtodo -= nrgot; \ nrdone += nrgot; \ data += nrgot; \ } \ dataPtr->putStorage (data, deleteIt); \ } \ void StManColumn::aips_name2(putScalarColumn,NM) (const Vector* dataPtr) \ { \ Bool deleteIt; \ const T* data = dataPtr->getStorage (deleteIt); \ aips_name2(putBlock,NM) (0, dataPtr->nelements(), data); \ dataPtr->freeStorage (data, deleteIt); \ } \ uInt StManColumn::aips_name2(getBlock,NM) \ (uInt rownr, uInt nrmax, T* dataPtr) \ { \ if (nrmax > 0) { \ aips_name2(get,NM) (rownr, dataPtr); \ return 1; \ } \ return 0; \ } \ void StManColumn::aips_name2(putBlock,NM) \ (uInt rownr, uInt nrmax, const T* dataPtr) \ { \ while (nrmax > 0) { \ aips_name2(put,NM) (rownr++, dataPtr++); \ nrmax--; \ } \ } \ void StManColumn::aips_name2(getArray,NM) (uInt, Array*) \ { throwGetArray(); } \ void StManColumn::aips_name2(putArray,NM) (uInt, const Array*) \ { throwPutArray(); } \ void StManColumn::aips_name2(getSlice,NM) (uInt, const Slicer&, Array*) \ { throwGetArray(); } \ void StManColumn::aips_name2(putSlice,NM) (uInt, const Slicer&, const Array*) \ { throwPutArray(); } \ void StManColumn::aips_name2(getArrayColumn,NM) (Array*) \ { throwGetArray(); } \ void StManColumn::aips_name2(putArrayColumn,NM) (const Array*) \ { throwPutArray(); } \ void StManColumn::aips_name2(getColumnSlice,NM) (const Slicer&, Array*) \ { throwGetArray(); } \ void StManColumn::aips_name2(putColumnSlice,NM) (const Slicer&, const Array*) \ { throwPutArray(); } \ void StManColumn::aips_name2(getScalarColumnCells,NM) \ (const RefRows& rownrs, \ Vector* values) \ { \ Vector& value = *values; \ uInt i=0; \ RefRowsSliceIter rowiter(rownrs); \ while (! rowiter.pastEnd()) { \ uInt rownr = rowiter.sliceStart(); \ uInt end = rowiter.sliceEnd(); \ uInt incr = rowiter.sliceIncr(); \ while (rownr <= end) { \ aips_name2(get,NM) (rownr, &(value(i++))); \ rownr += incr; \ } \ rowiter++; \ } \ } \ void StManColumn::aips_name2(putScalarColumnCells,NM) \ (const RefRows& rownrs, \ const Vector* values) \ { \ const Vector& value = *values; \ uInt i=0; \ RefRowsSliceIter rowiter(rownrs); \ while (! rowiter.pastEnd()) { \ uInt rownr = rowiter.sliceStart(); \ uInt end = rowiter.sliceEnd(); \ uInt incr = rowiter.sliceIncr(); \ while (rownr <= end) { \ aips_name2(put,NM) (rownr, &(value(i++))); \ rownr += incr; \ } \ rowiter++; \ } \ } \ void StManColumn::aips_name2(getArrayColumnCells,NM) \ (const RefRows& rownrs, \ Array* values) \ { \ Array& value = *values; \ ArrayIterator iter(value, value.ndim()-1); \ RefRowsSliceIter rowiter(rownrs); \ while (! rowiter.pastEnd()) { \ uInt rownr = rowiter.sliceStart(); \ uInt end = rowiter.sliceEnd(); \ uInt incr = rowiter.sliceIncr(); \ while (rownr <= end) { \ if (! isFixedShape()) { \ if (! iter.array().shape().isEqual (shape(rownr))) { \ throw DataManError("getArrayColumnCells shape mismatch" \ " for column " + columnName()); \ } \ } \ aips_name2(getArray,NM) (rownr, &(iter.array())); \ rownr += incr; \ iter.next(); \ } \ rowiter++; \ } \ } \ void StManColumn::aips_name2(putArrayColumnCells,NM) \ (const RefRows& rownrs, \ const Array* values) \ { \ const Array& value = *values; \ ReadOnlyArrayIterator iter(value, value.ndim()-1); \ RefRowsSliceIter rowiter(rownrs); \ while (! rowiter.pastEnd()) { \ uInt rownr = rowiter.sliceStart(); \ uInt end = rowiter.sliceEnd(); \ uInt incr = rowiter.sliceIncr(); \ while (rownr <= end) { \ aips_name2(putArray,NM) (rownr, &(iter.array())); \ rownr += incr; \ iter.next(); \ } \ rowiter++; \ } \ } \ void StManColumn::aips_name2(getColumnSliceCells,NM) \ (const RefRows& rownrs, \ const Slicer& ns, \ Array* values) \ { \ Array& value = *values; \ ArrayIterator iter(value, value.ndim()-1); \ RefRowsSliceIter rowiter(rownrs); \ while (! rowiter.pastEnd()) { \ uInt rownr = rowiter.sliceStart(); \ uInt end = rowiter.sliceEnd(); \ uInt incr = rowiter.sliceIncr(); \ while (rownr <= end) { \ aips_name2(getSlice,NM) (rownr, ns, &(iter.array())); \ rownr += incr; \ iter.next(); \ } \ rowiter++; \ } \ } \ void StManColumn::aips_name2(putColumnSliceCells,NM) \ (const RefRows& rownrs, \ const Slicer& ns, \ const Array* values) \ { \ const Array& value = *values; \ ReadOnlyArrayIterator iter(value, value.ndim()-1); \ RefRowsSliceIter rowiter(rownrs); \ while (! rowiter.pastEnd()) { \ uInt rownr = rowiter.sliceStart(); \ uInt end = rowiter.sliceEnd(); \ uInt incr = rowiter.sliceIncr(); \ while (rownr <= end) { \ aips_name2(putSlice,NM) (rownr, ns, &(iter.array())); \ rownr += incr; \ iter.next(); \ } \ rowiter++; \ } \ } 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(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-2.4.1/tables/DataMan/StManColumn.h000066400000000000000000001111721321422335000204240ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef TABLES_STMANCOLUMN_H #define TABLES_STMANCOLUMN_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations template class Vector; // // 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 abstract base class to handle a column in all // kind of storage managers. It is derived from DataManagerColumn // and implements several virtual functions for derived storage // manager column classes (like StManColumnAipsIO). // // All get and put functions (except for single scalars) in the abstract // base class DataManagerColumn have a generic void* data argument. // This is done to allow arbitrary typed arguments. This can be done // because the data type of the derived class always matches the // type of the data argument. // However, at one time the void* has to be casted to the exact type. // Storage managers only support the standard data types; therefore // it is possible to do the cast in a base class. This concentrates // the burden in one class and allows the derived classes to work // with correctly typed arguments. // The price is an extra virtual function call, but that is not a // problem for (expensive) operations on arrays. // It is not done for single scalars, because that price may be too high. // // See StManColumnAipsIO for the get/put functions required in a storage // manager column class handling scalars. // See StManColumnArrayAipsIO for the get/put functions required in a // storage manager column class handling arrays. This class also // contains the shape functions for direct arrays, while // StManColumnIndArrayAipsIO contains the shape functions for indirec // arrays. // // StManColumn also contains the data type of the column (which it // gets from the derived classes at construction time) and implements // the function dataType on behalf of its derived classes. // // // Making life easier for the derived classes. // // //# A List of bugs, limitations, extensions or planned refinements. // class StManColumn : public DataManagerColumn { public: // Default constructor. StManColumn (int dataType); ~StManColumn(); // 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. int dataType() const; // By default the storage manager can handle access to a scalar column. Bool canAccessScalarColumn (Bool& reask) const; // All storage managers can handle access to scalar column cells, because // this class contains a default implementation of getScalarColumnCellsV. Bool canAccessScalarColumnCells (Bool& reask) const; // All storage managers can handle access to array column cells, because // this class contains a default implementation of getArrayColumnCellsV. Bool canAccessArrayColumnCells (Bool& reask) const; // Get all scalar values in the column. // The argument dataPtr is in fact a Vector*, but a void* // 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. void getScalarColumnV (void* dataPtr); // Put all scalar values in the column. // The argument dataPtr is in fact a const Vector*, but a const void* // 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. void putScalarColumnV (const void* dataPtr); // Get some scalar values in the column. // The argument dataPtr is in fact a Vector*, but a void* // 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. void getScalarColumnCellsV (const RefRows& rownrs, void* dataPtr); // Put some scalar values in the column. // The argument dataPtr is in fact a const Vector*, but a const void* // 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. void putScalarColumnCellsV (const RefRows& rownrs, const void* dataPtr); // Get scalars from the given row on with a maximum of nrmax values. // It returns the actual number of values got. // This can be used to get an entire column of scalars or to get // a part of a column (for a cache for example). // The argument dataPtr is in fact a T*, but a void* // is needed to be generic. // The default implementation calls the appropriate getBlockXXV function. uInt getBlockV (uInt rownr, uInt nrmax, void* dataPtr); // Put nrmax scalars from the given row on. // It returns the actual number of values put. // This can be used to put an entire column of scalars or to put // a part of a column (for a cache for example). // The argument dataPtr is in fact a const T*, but a const void* // is needed to be generic. // The default implementation calls the appropriate putBlockXXV function. void putBlockV (uInt rownr, uInt nrmax, const void* dataPtr); // Get the array value in the given row. // The argument dataPtr is in fact an Array*, but a void* // 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. void getArrayV (uInt rownr, void* dataPtr); // Put the array value into the given row. // The argument dataPtr is in fact a const Array*, but a const void* // 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. void putArrayV (uInt rownr, const void* dataPtr); // Get all array values in the column. // The argument dataPtr is in fact an Array*, but a void* // 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. void getArrayColumnV (void* dataPtr); // Put all array values in the column. // The argument dataPtr is in fact a const Array*, but a const void* // 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. void putArrayColumnV (const void* dataPtr); // Get some array values in the column. // The argument dataPtr is in fact an Array*, but a void* // 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. void getArrayColumnCellsV (const RefRows& rownrs, void* dataPtr); // Put some array values in the column. // The argument dataPtr is in fact an const Array*, but a const void* // 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. void putArrayColumnCellsV (const RefRows& rownrs, const void* dataPtr); // Get a section of the array in the given row. // The argument dataPtr is in fact an Array*, but a void* // 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. void getSliceV (uInt rownr, const Slicer& slicer, void* dataPtr); // Put into a section of the array in the given row. // The argument dataPtr is in fact a const Array*, but a const void* // 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. void putSliceV (uInt rownr, const Slicer& slicer, const void* dataPtr); // Get a section of all arrays in the column. // The argument dataPtr is in fact an Array*, but a void* // 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. void getColumnSliceV (const Slicer& slicer, void* dataPtr); // Put into a section of all arrays in the column. // The argument dataPtr is in fact a const Array*, but a const void* // 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. void putColumnSliceV (const Slicer& slicer, const void* dataPtr); // Get a section of some arrays in the column. // The argument dataPtr is in fact an Array*, but a void* // 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, void* dataPtr); // Put into a section of some arrays in the column. // The argument dataPtr is in fact a const Array*, but a const void* // 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 void* dataPtr); private: // The object cannot be copied. StManColumn (const StManColumn&); // The object cannot be assigned to. StManColumn& operator= (const StManColumn&); // Throw an "invalid operation" exception for the default // implementation of getArray. void throwGetArray() const; // Throw an "invalid operation" exception for the default // implementation of putArray. void throwPutArray() const; protected: // 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 implementation uses the corresponding getBlockXXXV. // 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 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 implementation uses the corresponding putBlockXXXV. // 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 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 implementation loops through all rows. // 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 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 implementation loops through all rows. // 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 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 scalars from the given row on with a maximum of nrmax values. // This can be used to get an entire column of scalars or to get // a part of a column (for a cache for example). // The buffer pointed to by dataPtr has to have the length nrmax. // (which is guaranteed by the ScalarColumn get function). // The default implementation gets one value. // virtual uInt getBlockBoolV (uInt rownr, uInt nrmax, Bool* dataPtr); virtual uInt getBlockuCharV (uInt rownr, uInt nrmax, uChar* dataPtr); virtual uInt getBlockShortV (uInt rownr, uInt nrmax, Short* dataPtr); virtual uInt getBlockuShortV (uInt rownr, uInt nrmax, uShort* dataPtr); virtual uInt getBlockIntV (uInt rownr, uInt nrmax, Int* dataPtr); virtual uInt getBlockuIntV (uInt rownr, uInt nrmax, uInt* dataPtr); virtual uInt getBlockfloatV (uInt rownr, uInt nrmax, float* dataPtr); virtual uInt getBlockdoubleV (uInt rownr, uInt nrmax, double* dataPtr); virtual uInt getBlockComplexV (uInt rownr, uInt nrmax, Complex* dataPtr); virtual uInt getBlockDComplexV (uInt rownr, uInt nrmax, DComplex* dataPtr); virtual uInt getBlockStringV (uInt rownr, uInt nrmax, String* dataPtr); // // Put nrmax scalars from the given row on. // This can be used to put an entire column of scalars or to put // a part of a column (for a cache for example). // The buffer pointed to by dataPtr has to have the length nrmax. // The default implementation puts one value at the time. // virtual void putBlockBoolV (uInt rownr, uInt nrmax, const Bool* dataPtr); virtual void putBlockuCharV (uInt rownr, uInt nrmax, const uChar* dataPtr); virtual void putBlockShortV (uInt rownr, uInt nrmax, const Short* dataPtr); virtual void putBlockuShortV (uInt rownr, uInt nrmax, const uShort* dataPtr); virtual void putBlockIntV (uInt rownr, uInt nrmax, const Int* dataPtr); virtual void putBlockuIntV (uInt rownr, uInt nrmax, const uInt* dataPtr); virtual void putBlockfloatV (uInt rownr, uInt nrmax, const float* dataPtr); virtual void putBlockdoubleV (uInt rownr, uInt nrmax, const double* dataPtr); virtual void putBlockComplexV (uInt rownr, uInt nrmax, const Complex* dataPtr); virtual void putBlockDComplexV (uInt rownr, uInt nrmax, const DComplex* dataPtr); virtual void putBlockStringV (uInt rownr, uInt nrmax, const String* 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 implementation loops through all rows. // 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 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 implementation loops through all rows. // 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 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 implementation uses the corresponding getBlockXXXV. // 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 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 implementation uses the corresponding putBlockXXXV. // 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 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 implementation throws an "invalid operation exception". // 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 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 implementation throws an "invalid operation exception". // 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 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 implementation throws an "invalid operation exception". // 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 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 implementation throws an "invalid operation exception". // 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 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 implementation uses the corresponding getBlockXXXV. // 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 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 implementation uses the corresponding putBlockXXXV. // 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 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 implementation throws an "invalid operation exception". // 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 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 implementation throws an "invalid operation exception". // 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 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); // private: // The data type of the column. int dtype_p; }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/tables/DataMan/StandardStMan.cc000066400000000000000000000032231321422335000210620ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/tables/DataMan/StandardStMan.h000066400000000000000000000172601321422335000207320ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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(); private: // Copy constructor cannot be used. StandardStMan (const StandardStMan& that); // Assignment cannot be used. StandardStMan& operator= (const StandardStMan& that); }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/tables/DataMan/StandardStManAccessor.cc000066400000000000000000000062301321422335000225460ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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-2.4.1/tables/DataMan/StandardStManAccessor.h000066400000000000000000000126641321422335000224200ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/tables/DataMan/TSMColumn.cc000066400000000000000000000054651321422335000202120ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# Includes #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN TSMColumn::TSMColumn (TiledStMan* stman, int dataType, const String& columnName) : StManColumn (dataType), stmanPtr_p (stman), dtype_p (dataType), name_p (columnName), colPtr_p (0) {} TSMColumn::TSMColumn (const TSMColumn& that) : StManColumn (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-2.4.1/tables/DataMan/TSMColumn.h000066400000000000000000000125601321422335000200460ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 StManColumn { 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(); // 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); private: // Forbid assignment. TSMColumn& operator= (const TSMColumn&); }; inline const String& TSMColumn::columnName() const { return name_p; } inline const IPosition& TSMColumn::shapeColumn() const { return columnShape_p; } } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/tables/DataMan/TSMCoordColumn.cc000066400000000000000000000177441321422335000212040ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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 (uInt 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 (uInt 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 (uInt 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::getfloatV (uInt 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::putfloatV (uInt 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::getArrayfloatV (uInt rownr, Array* 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); hypercube->valueRecord().get (columnName(), *dataPtr); } void TSMCoordColumn::putArrayfloatV (uInt rownr, const Array* 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); hypercube->rwValueRecord().define (columnName(), *dataPtr); stmanPtr_p->setDataChanged(); } #define TSMCOORDCOLUMN_GETPUT(T,NM) \ void TSMCoordColumn::aips_name2(get,NM) (uInt 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,NM) (uInt 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(); \ } \ void TSMCoordColumn::aips_name2(getArray,NM) (uInt rownr, Array* dataPtr) \ { \ IPosition position; \ TSMCube* hypercube = stmanPtr_p->getHypercube (rownr, position); \ hypercube->valueRecord().get (columnName(), *dataPtr); \ } \ void TSMCoordColumn::aips_name2(putArray,NM) (uInt rownr, const Array* dataPtr) \ { \ IPosition position; \ TSMCube* hypercube = stmanPtr_p->getHypercube (rownr, position); \ hypercube->rwValueRecord().define (columnName(), *dataPtr); \ stmanPtr_p->setDataChanged(); \ } TSMCOORDCOLUMN_GETPUT(Int,IntV) TSMCOORDCOLUMN_GETPUT(uInt,uIntV) //#TSMCOORDCOLUMN_GETPUT(float,floatV) TSMCOORDCOLUMN_GETPUT(double,doubleV) TSMCOORDCOLUMN_GETPUT(Complex,ComplexV) TSMCOORDCOLUMN_GETPUT(DComplex,DComplexV) } //# NAMESPACE CASACORE - END casacore-2.4.1/tables/DataMan/TSMCoordColumn.h000066400000000000000000000147421321422335000210410ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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(); // Set the shape of the coordinate vector in the given row. void setShape (uInt rownr, const IPosition& shape); // Is the value shape defined in the given row? Bool isShapeDefined (uInt rownr); // Get the shape of the item in the given row. IPosition shape (uInt 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). // void getIntV (uInt rownr, Int* dataPtr); void getuIntV (uInt rownr, uInt* dataPtr); void getfloatV (uInt rownr, float* dataPtr); void getdoubleV (uInt rownr, double* dataPtr); void getComplexV (uInt rownr, Complex* dataPtr); void getDComplexV (uInt 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). // void putIntV (uInt rownr, const Int* dataPtr); void putuIntV (uInt rownr, const uInt* dataPtr); void putfloatV (uInt rownr, const float* dataPtr); void putdoubleV (uInt rownr, const double* dataPtr); void putComplexV (uInt rownr, const Complex* dataPtr); void putDComplexV (uInt 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). // The default implementation thrown an "invalid operation exception". // void getArrayIntV (uInt rownr, Array* dataPtr); void getArrayuIntV (uInt rownr, Array* dataPtr); void getArrayfloatV (uInt rownr, Array* dataPtr); void getArraydoubleV (uInt rownr, Array* dataPtr); void getArrayComplexV (uInt rownr, Array* dataPtr); void getArrayDComplexV (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 implementation thrown an "invalid operation exception". // void putArrayIntV (uInt rownr, const Array* dataPtr); void putArrayuIntV (uInt rownr, const Array* dataPtr); void putArrayfloatV (uInt rownr, const Array* dataPtr); void putArraydoubleV (uInt rownr, const Array* dataPtr); void putArrayComplexV (uInt rownr, const Array* dataPtr); void putArrayDComplexV (uInt rownr, const Array* dataPtr); // private: // The axis number of the coordinate. uInt axisNr_p; // Forbid copy constructor. TSMCoordColumn (const TSMCoordColumn&); // Forbid assignment. TSMCoordColumn& operator= (const TSMCoordColumn&); }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/tables/DataMan/TSMCube.cc000066400000000000000000001440261321422335000176300ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: TSMCube.cc 21521 2014-12-10 08:06:42Z gervandiepen $ //# 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 // 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() << 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 (uInt 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) { uInt 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 maxSize, uInt bucketSize) { // An overdraft of 10% is allowed. if (maxSize > 0 && cacheSize * bucketSize > maxSize) { uInt size = maxSize / bucketSize; if (10 * cacheSize > 11 * size) { return size; } } 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 and if the entire cube needs to be cached, // do not cache if more than 20% of the memory is needed. if (!userSet && cacheSize >= nrTiles_p) { uInt maxSize = uInt(HostInfo::memoryTotal() * 1024.*0.2 / 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. uInt 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(); // 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 (which is the length of a tile in external format). uInt bucketSize() const; // Get the length of a tile 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 (uInt 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 maxCacheSize, 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 if smaller // than the maximum cache size. Otherwise the maximum is returned. // uInt validateCacheSize (uInt cacheSize) const; static uInt validateCacheSize (uInt cacheSize, uInt maxSize, 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: // Forbid copy constructor. TSMCube (const TSMCube&); // Forbid assignment. TSMCube& operator= (const TSMCube&); // 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 pixels. 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-2.4.1/tables/DataMan/TSMCubeBuff.cc000066400000000000000000000432741321422335000204360ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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, uInt bufferSize) : TSMCube (stman, file, cubeShape, tileShape, values, fileOffset, True), cache_p (0), bufferSize_p (bufferSize) { // 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, uInt bufferSize) : TSMCube (stman, ios, True), cache_p (0), bufferSize_p (bufferSize) {} TSMCubeBuff::~TSMCubeBuff() { delete cache_p; } void TSMCubeBuff::showCacheStatistics (ostream& os) const { if (cache_p != 0) { os << ">>> No TSMCube cache statistics (uses buffered IO)" << endl; os << "<<<" << endl; } } void TSMCubeBuff::makeCache() { // If there is no cache, make one. if (cache_p == 0) { cache_p = new BucketBuffered (filePtr_p->bucketFile(), fileOffset_p, bucketSize_p, nrTiles_p); } } void TSMCubeBuff::flushCache() { if (cache_p != 0) { cache_p->flush(); } } void TSMCubeBuff::resyncCache() { if (cache_p != 0) { cache_p->resync (nrTiles_p); } } void TSMCubeBuff::deleteCache() { delete cache_p; cache_p = 0; } void TSMCubeBuff::setShape (const IPosition& cubeShape, const IPosition& tileShape) { TSMCube::setShape (cubeShape, tileShape); makeCache(); } void TSMCubeBuff::extend (uInt 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-2.4.1/tables/DataMan/TSMCubeBuff.h000066400000000000000000000166771321422335000203070ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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, uInt bufferSize); // 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, uInt bufferSize); virtual ~TSMCubeBuff(); // 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 (uInt 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: // Forbid copy constructor. TSMCubeBuff (const TSMCubeBuff&); // Forbid assignment. TSMCubeBuff& operator= (const TSMCubeBuff&); // Get the cache object. // This will construct the cache object if not present yet. BucketBuffered* 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. BucketBuffered* cache_p; // The buffer size to use. uInt bufferSize_p; }; inline BucketBuffered* TSMCubeBuff::getCache() { if (cache_p == 0) { makeCache(); } return cache_p; } } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/tables/DataMan/TSMCubeMMap.cc000066400000000000000000000372601321422335000204040ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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 (uInt 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-2.4.1/tables/DataMan/TSMCubeMMap.h000066400000000000000000000165521321422335000202470ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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(); // 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 (uInt 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: // Forbid copy constructor. TSMCubeMMap (const TSMCubeMMap&); // Forbid assignment. TSMCubeMMap& operator= (const TSMCubeMMap&); // 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-2.4.1/tables/DataMan/TSMDataColumn.cc000066400000000000000000000707551321422335000210100ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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() {} uInt TSMDataColumn::dataLength (uInt nrPixels) const { // For Bools a byte can hold 8 pixels. if (tilePixelSize_p == 0) { return (nrPixels + 7) / 8; } return tilePixelSize_p * nrPixels; } Bool TSMDataColumn::canAccessScalarColumn (Bool& reask) const { return stmanPtr_p->canAccessColumn (reask); } Bool TSMDataColumn::canAccessArrayColumn (Bool& reask) const { return stmanPtr_p->canAccessColumn (reask); } Bool TSMDataColumn::canAccessSlice (Bool& reask) const { reask = False; return True; } Bool TSMDataColumn::canAccessColumnSlice (Bool& reask) const { return stmanPtr_p->canAccessColumn (reask); } 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 (uInt rownr, const IPosition& shape) { setShapeTiled (rownr, shape, stmanPtr_p->defaultTileShape()); } void TSMDataColumn::setShapeTiled (uInt 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 (uInt 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 (uInt 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 (uInt 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 (uInt 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()); uInt i; for (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 (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); uInt chunkSize = arrShape.product() / arrShape(lastAxis) * localPixelSize_p; uInt nrinc = 0; Int 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()) { uInt rownr = iter.sliceStart(); uInt erow = iter.sliceEnd(); uInt 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); Int 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) { 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; uInt chunkSize = arrShape.product() / arrShape(lastAxis) * localPixelSize_p; uInt nrinc = 0; Int 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()) { uInt rownr = iter.sliceStart(); uInt erow = iter.sliceEnd(); uInt 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); Int 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::getfloatV (uInt rownr, float* dataPtr) { accessCell (rownr, dataPtr, False); } void TSMDataColumn::putfloatV (uInt rownr, const float* dataPtr) { accessCell (rownr, dataPtr, True); } void TSMDataColumn::getArrayfloatV (uInt rownr, Array* dataPtr) { Bool deleteIt; float* data = dataPtr->getStorage (deleteIt); accessCell (rownr, data, False); dataPtr->putStorage (data, deleteIt); } void TSMDataColumn::putArrayfloatV (uInt rownr, const Array* dataPtr) { Bool deleteIt; const float* data = dataPtr->getStorage (deleteIt); accessCell (rownr, data, True); dataPtr->freeStorage (data, deleteIt); } void TSMDataColumn::getSlicefloatV (uInt rownr, const Slicer& ns, Array* dataPtr) { Bool deleteIt; float* data = dataPtr->getStorage (deleteIt); accessCellSlice (rownr, ns, data, False); dataPtr->putStorage (data, deleteIt); } void TSMDataColumn::putSlicefloatV (uInt rownr, const Slicer& ns, const Array* dataPtr) { Bool deleteIt; const float* data = dataPtr->getStorage (deleteIt); accessCellSlice (rownr, ns, data, True); dataPtr->freeStorage (data, deleteIt); } void TSMDataColumn::getScalarColumnfloatV (Vector* dataPtr) { TSMDataColumn::getArrayColumnfloatV (dataPtr); } void TSMDataColumn::putScalarColumnfloatV (const Vector* dataPtr) { TSMDataColumn::putArrayColumnfloatV (dataPtr); } void TSMDataColumn::getScalarColumnCellsfloatV (const RefRows& rownrs, Vector* dataPtr) { // Only use optimized accessColumnCells for hypercubes where the rows // are mapped to a single axis. if (dataPtr->ndim() == stmanPtr_p->nrCoordVector() + 1) { TSMDataColumn::getArrayColumnCellsfloatV (rownrs, dataPtr); } else { StManColumn::getScalarColumnCellsfloatV (rownrs, dataPtr); } } void TSMDataColumn::putScalarColumnCellsfloatV (const RefRows& rownrs, const Vector* dataPtr) { // Only use optimized accessColumnCells for hypercubes where the rows // are mapped to a single axis. if (dataPtr->ndim() == stmanPtr_p->nrCoordVector() + 1) { TSMDataColumn::putArrayColumnCellsfloatV (rownrs, dataPtr); } else { StManColumn::putScalarColumnCellsfloatV (rownrs, dataPtr); } } void TSMDataColumn::getArrayColumnfloatV (Array* dataPtr) { Bool deleteIt; float* data = dataPtr->getStorage (deleteIt); accessColumn (data, False); dataPtr->putStorage (data, deleteIt); } void TSMDataColumn::putArrayColumnfloatV (const Array* dataPtr) { Bool deleteIt; const float* data = dataPtr->getStorage (deleteIt); accessColumn (data, True); dataPtr->freeStorage (data, deleteIt); } void TSMDataColumn::getColumnSlicefloatV (const Slicer& ns, Array* dataPtr) { Bool deleteIt; float* data = dataPtr->getStorage (deleteIt); accessColumnSlice (ns, data, False); dataPtr->putStorage (data, deleteIt); } void TSMDataColumn::putColumnSlicefloatV (const Slicer& ns, const Array* dataPtr) { Bool deleteIt; const float* data = dataPtr->getStorage (deleteIt); accessColumnSlice (ns, data, True); dataPtr->freeStorage (data, deleteIt); } void TSMDataColumn::getArrayColumnCellsfloatV (const RefRows& rownrs, Array* 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; float* data = dataPtr->getStorage (deleteIt); accessColumnCells (rownrs, dataPtr->shape(), data, False); dataPtr->putStorage (data, deleteIt); } else { StManColumn::getArrayColumnCellsfloatV (rownrs, dataPtr); } } void TSMDataColumn::putArrayColumnCellsfloatV (const RefRows& rownrs, const Array* 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 float* data = dataPtr->getStorage (deleteIt); accessColumnCells (rownrs, dataPtr->shape(), data, True); dataPtr->freeStorage (data, deleteIt); } else { StManColumn::putArrayColumnCellsfloatV (rownrs, dataPtr); } } void TSMDataColumn::getColumnSliceCellsfloatV (const RefRows& rownrs, const Slicer& ns, Array* 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; float* data = dataPtr->getStorage (deleteIt); accessColumnSliceCells (rownrs, ns, dataPtr->shape(), data, False); dataPtr->putStorage (data, deleteIt); } else { StManColumn::getColumnSliceCellsfloatV (rownrs, ns, dataPtr); } } void TSMDataColumn::putColumnSliceCellsfloatV (const RefRows& rownrs, const Slicer& ns, const Array* 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 float* data = dataPtr->getStorage (deleteIt); accessColumnSliceCells (rownrs, ns, dataPtr->shape(), data, True); dataPtr->freeStorage (data, deleteIt); } else { StManColumn::putColumnSliceCellsfloatV (rownrs, ns, dataPtr); } } #define TSMDATACOLUMN_GETPUT(T,NM) \ void TSMDataColumn::aips_name2(get,NM) (uInt rownr, T* dataPtr) \ { \ accessCell (rownr, dataPtr, False); \ } \ void TSMDataColumn::aips_name2(put,NM) (uInt rownr, const T* dataPtr) \ { \ accessCell (rownr, dataPtr, True); \ } \ void TSMDataColumn::aips_name2(getArray,NM) (uInt rownr, Array* dataPtr) \ { \ Bool deleteIt; \ T* data = dataPtr->getStorage (deleteIt); \ accessCell (rownr, data, False); \ dataPtr->putStorage (data, deleteIt); \ } \ void TSMDataColumn::aips_name2(putArray,NM) (uInt rownr, const Array* dataPtr) \ { \ Bool deleteIt; \ const T* data = dataPtr->getStorage (deleteIt); \ accessCell (rownr, data, True); \ dataPtr->freeStorage (data, deleteIt); \ } \ void TSMDataColumn::aips_name2(getSlice,NM) (uInt rownr, const Slicer& ns, \ Array* dataPtr) \ { \ Bool deleteIt; \ T* data = dataPtr->getStorage (deleteIt); \ accessCellSlice (rownr, ns, data, False); \ dataPtr->putStorage (data, deleteIt); \ } \ void TSMDataColumn::aips_name2(putSlice,NM) (uInt rownr, const Slicer& ns, \ const Array* dataPtr) \ { \ Bool deleteIt; \ const T* data = dataPtr->getStorage (deleteIt); \ accessCellSlice (rownr, ns, data, True); \ dataPtr->freeStorage (data, deleteIt); \ } \ void TSMDataColumn::aips_name2(getScalarColumn,NM) (Vector* dataPtr) \ { \ TSMDataColumn::aips_name2(getArrayColumn,NM) (dataPtr); \ } \ void TSMDataColumn::aips_name2(putScalarColumn,NM) (const Vector* dataPtr) \ { \ TSMDataColumn::aips_name2(putArrayColumn,NM) (dataPtr); \ } \ void TSMDataColumn::aips_name2(getArrayColumn,NM) (Array* dataPtr) \ { \ Bool deleteIt; \ T* data = dataPtr->getStorage (deleteIt); \ accessColumn (data, False); \ dataPtr->putStorage (data, deleteIt); \ } \ void TSMDataColumn::aips_name2(putArrayColumn,NM) (const Array* dataPtr) \ { \ Bool deleteIt; \ const T* data = dataPtr->getStorage (deleteIt); \ accessColumn (data, True); \ dataPtr->freeStorage (data, deleteIt ); \ } \ void TSMDataColumn::aips_name2(getColumnSlice,NM) (const Slicer& ns, \ Array* dataPtr) \ { \ Bool deleteIt; \ T* data = dataPtr->getStorage (deleteIt); \ accessColumnSlice (ns, data, False); \ dataPtr->putStorage (data, deleteIt); \ } \ void TSMDataColumn::aips_name2(putColumnSlice,NM) (const Slicer& ns, \ const Array* dataPtr) \ { \ Bool deleteIt; \ const T* data = dataPtr->getStorage (deleteIt); \ accessColumnSlice (ns, data, True); \ dataPtr->freeStorage (data, deleteIt); \ } \ void TSMDataColumn::aips_name2(getScalarColumnCells,NM)(const RefRows& rownrs,\ Vector* dataPtr) \ { \ if (dataPtr->ndim() == stmanPtr_p->nrCoordVector() + 1) { \ TSMDataColumn::aips_name2(getArrayColumnCells,NM) (rownrs, dataPtr); \ } else { \ StManColumn::aips_name2(getScalarColumnCells,NM) (rownrs, dataPtr); \ } \ } \ void TSMDataColumn::aips_name2(putScalarColumnCells,NM)(const RefRows& rownrs,\ const Vector* dataPtr) \ { \ if (dataPtr->ndim() == stmanPtr_p->nrCoordVector() + 1) { \ TSMDataColumn::aips_name2(putArrayColumnCells,NM) (rownrs, dataPtr); \ } else { \ StManColumn::aips_name2(putScalarColumnCells,NM) (rownrs, dataPtr); \ } \ } \ void TSMDataColumn::aips_name2(getArrayColumnCells,NM)(const RefRows& rownrs, \ Array* dataPtr) \ { \ if (dataPtr->ndim() == stmanPtr_p->nrCoordVector() + 1) { \ Bool deleteIt; \ T* data = dataPtr->getStorage (deleteIt); \ accessColumnCells (rownrs, dataPtr->shape(), data, False); \ dataPtr->putStorage (data, deleteIt); \ } else { \ StManColumn::aips_name2(getArrayColumnCells,NM) (rownrs, dataPtr); \ } \ } \ void TSMDataColumn::aips_name2(putArrayColumnCells,NM)(const RefRows& rownrs, \ const Array* dataPtr) \ { \ if (dataPtr->ndim() == stmanPtr_p->nrCoordVector() + 1) { \ Bool deleteIt; \ const T* data = dataPtr->getStorage (deleteIt); \ accessColumnCells (rownrs, dataPtr->shape(), data, True); \ dataPtr->freeStorage (data, deleteIt); \ } else { \ StManColumn::aips_name2(putArrayColumnCells,NM) (rownrs, dataPtr); \ } \ } \ void TSMDataColumn::aips_name2(getColumnSliceCells,NM)(const RefRows& rownrs, \ const Slicer& ns, \ Array* dataPtr) \ { \ if (dataPtr->ndim() == stmanPtr_p->nrCoordVector() + 1) { \ Bool deleteIt; \ T* data = dataPtr->getStorage (deleteIt); \ accessColumnSliceCells (rownrs, ns, dataPtr->shape(), data, False); \ dataPtr->putStorage (data, deleteIt); \ } else { \ StManColumn::aips_name2(getColumnSliceCells,NM) (rownrs, ns, dataPtr);\ } \ } \ void TSMDataColumn::aips_name2(putColumnSliceCells,NM)(const RefRows& rownrs, \ const Slicer& ns, \ const Array* dataPtr) \ { \ if (dataPtr->ndim() == stmanPtr_p->nrCoordVector() + 1) { \ Bool deleteIt; \ const T* data = dataPtr->getStorage (deleteIt); \ accessColumnSliceCells (rownrs, ns, dataPtr->shape(), data, True); \ dataPtr->freeStorage (data, deleteIt); \ } else { \ StManColumn::aips_name2(putColumnSliceCells,NM) (rownrs, ns, dataPtr);\ } \ } TSMDATACOLUMN_GETPUT(Bool,BoolV) TSMDATACOLUMN_GETPUT(uChar,uCharV) TSMDATACOLUMN_GETPUT(Short,ShortV) TSMDATACOLUMN_GETPUT(uShort,uShortV) TSMDATACOLUMN_GETPUT(Int,IntV) TSMDATACOLUMN_GETPUT(uInt,uIntV) //#TSMDATACOLUMN_GETPUT(float,floatV) TSMDATACOLUMN_GETPUT(double,doubleV) TSMDATACOLUMN_GETPUT(Complex,ComplexV) TSMDATACOLUMN_GETPUT(DComplex,DComplexV) } //# NAMESPACE CASACORE - END casacore-2.4.1/tables/DataMan/TSMDataColumn.h000066400000000000000000000657151321422335000206520ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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(); // 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. uInt dataLength (uInt nrPixels) const; // Set column sequence number. void setColumnNumber (uInt colnr); // It can handle access to a scalar column if there is one hypercube. Bool canAccessScalarColumn (Bool& reask) const; // It can handle access to an array column if there is one hypercube. Bool canAccessArrayColumn (Bool& reask) const; // It can handle access to a slice in a cell. Bool canAccessSlice (Bool& reask) const; // It can handle access to a slice in column if there is one hypercube. Bool canAccessColumnSlice (Bool& reask) const; // 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 (uInt 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 (uInt rownr, const IPosition& shape, const IPosition& tileShape); // Is the value shape defined in the given row? Bool isShapeDefined (uInt rownr); // Get the shape of the item in the given row. IPosition shape (uInt rownr); // Get the tile shape of the item in the given row. IPosition tileShape (uInt 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). // void getBoolV (uInt rownr, Bool* dataPtr); void getuCharV (uInt rownr, uChar* dataPtr); void getShortV (uInt rownr, Short* dataPtr); void getuShortV (uInt rownr, uShort* dataPtr); void getIntV (uInt rownr, Int* dataPtr); void getuIntV (uInt rownr, uInt* dataPtr); void getfloatV (uInt rownr, float* dataPtr); void getdoubleV (uInt rownr, double* dataPtr); void getComplexV (uInt rownr, Complex* dataPtr); void getDComplexV (uInt 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). // void putBoolV (uInt rownr, const Bool* dataPtr); void putuCharV (uInt rownr, const uChar* dataPtr); void putShortV (uInt rownr, const Short* dataPtr); void putuShortV (uInt rownr, const uShort* dataPtr); void putIntV (uInt rownr, const Int* dataPtr); void putuIntV (uInt rownr, const uInt* dataPtr); void putfloatV (uInt rownr, const float* dataPtr); void putdoubleV (uInt rownr, const double* dataPtr); void putComplexV (uInt rownr, const Complex* dataPtr); void putDComplexV (uInt 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). // The default implementation thrown an "invalid operation exception". // void getArrayBoolV (uInt rownr, Array* dataPtr); void getArrayuCharV (uInt rownr, Array* dataPtr); void getArrayShortV (uInt rownr, Array* dataPtr); void getArrayuShortV (uInt rownr, Array* dataPtr); void getArrayIntV (uInt rownr, Array* dataPtr); void getArrayuIntV (uInt rownr, Array* dataPtr); void getArrayfloatV (uInt rownr, Array* dataPtr); void getArraydoubleV (uInt rownr, Array* dataPtr); void getArrayComplexV (uInt rownr, Array* dataPtr); void getArrayDComplexV (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 implementation thrown an "invalid operation exception". // void putArrayBoolV (uInt rownr, const Array* dataPtr); void putArrayuCharV (uInt rownr, const Array* dataPtr); void putArrayShortV (uInt rownr, const Array* dataPtr); void putArrayuShortV (uInt rownr, const Array* dataPtr); void putArrayIntV (uInt rownr, const Array* dataPtr); void putArrayuIntV (uInt rownr, const Array* dataPtr); void putArrayfloatV (uInt rownr, const Array* dataPtr); void putArraydoubleV (uInt rownr, const Array* dataPtr); void putArrayComplexV (uInt rownr, const Array* dataPtr); void putArrayDComplexV (uInt rownr, const Array* dataPtr); // void getSliceBoolV (uInt rownr, const Slicer& slicer, Array* dataPtr); void getSliceuCharV (uInt rownr, const Slicer& slicer, Array* dataPtr); void getSliceShortV (uInt rownr, const Slicer& slicer, Array* dataPtr); void getSliceuShortV (uInt rownr, const Slicer& slicer, Array* dataPtr); void getSliceIntV (uInt rownr, const Slicer& slicer, Array* dataPtr); void getSliceuIntV (uInt rownr, const Slicer& slicer, Array* dataPtr); void getSlicefloatV (uInt rownr, const Slicer& slicer, Array* dataPtr); void getSlicedoubleV (uInt rownr, const Slicer& slicer, Array* dataPtr); void getSliceComplexV (uInt rownr, const Slicer& slicer, Array* dataPtr); void getSliceDComplexV (uInt rownr, const Slicer& slicer, Array* dataPtr); void putSliceBoolV (uInt rownr, const Slicer& slicer, const Array* dataPtr); void putSliceuCharV (uInt rownr, const Slicer& slicer, const Array* dataPtr); void putSliceShortV (uInt rownr, const Slicer& slicer, const Array* dataPtr); void putSliceuShortV (uInt rownr, const Slicer& slicer, const Array* dataPtr); void putSliceIntV (uInt rownr, const Slicer& slicer, const Array* dataPtr); void putSliceuIntV (uInt rownr, const Slicer& slicer, const Array* dataPtr); void putSlicefloatV (uInt rownr, const Slicer& slicer, const Array* dataPtr); void putSlicedoubleV (uInt rownr, const Slicer& slicer, const Array* dataPtr); void putSliceComplexV (uInt rownr, const Slicer& slicer, const Array* dataPtr); void putSliceDComplexV (uInt rownr, const Slicer& slicer, const Array* dataPtr); void getScalarColumnBoolV (Vector* arr); void getScalarColumnuCharV (Vector* arr); void getScalarColumnShortV (Vector* arr); void getScalarColumnuShortV (Vector* arr); void getScalarColumnIntV (Vector* arr); void getScalarColumnuIntV (Vector* arr); void getScalarColumnfloatV (Vector* arr); void getScalarColumndoubleV (Vector* arr); void getScalarColumnComplexV (Vector* arr); void getScalarColumnDComplexV (Vector* arr); void putScalarColumnBoolV (const Vector* arr); void putScalarColumnuCharV (const Vector* arr); void putScalarColumnShortV (const Vector* arr); void putScalarColumnuShortV (const Vector* arr); void putScalarColumnIntV (const Vector* arr); void putScalarColumnuIntV (const Vector* arr); void putScalarColumnfloatV (const Vector* arr); void putScalarColumndoubleV (const Vector* arr); void putScalarColumnComplexV (const Vector* arr); void putScalarColumnDComplexV (const Vector* arr); // 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 implementation loops through all rows. // 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 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); // // 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 implementation loops through all rows. // 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 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); // void getArrayColumnBoolV (Array* arr); void getArrayColumnuCharV (Array* arr); void getArrayColumnShortV (Array* arr); void getArrayColumnuShortV (Array* arr); void getArrayColumnIntV (Array* arr); void getArrayColumnuIntV (Array* arr); void getArrayColumnfloatV (Array* arr); void getArrayColumndoubleV (Array* arr); void getArrayColumnComplexV (Array* arr); void getArrayColumnDComplexV (Array* arr); void putArrayColumnBoolV (const Array* arr); void putArrayColumnuCharV (const Array* arr); void putArrayColumnShortV (const Array* arr); void putArrayColumnuShortV (const Array* arr); void putArrayColumnIntV (const Array* arr); void putArrayColumnuIntV (const Array* arr); void putArrayColumnfloatV (const Array* arr); void putArrayColumndoubleV (const Array* arr); void putArrayColumnComplexV (const Array* arr); void putArrayColumnDComplexV (const Array* arr); // 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 implementation throws an "invalid operation exception". // 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 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); // // 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 implementation throws an "invalid operation exception". // 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 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); // void getColumnSliceBoolV (const Slicer& slicer, Array* arr); void getColumnSliceuCharV (const Slicer& slicer, Array* arr); void getColumnSliceShortV (const Slicer& slicer, Array* arr); void getColumnSliceuShortV (const Slicer& slicer, Array* arr); void getColumnSliceIntV (const Slicer& slicer, Array* arr); void getColumnSliceuIntV (const Slicer& slicer, Array* arr); void getColumnSlicefloatV (const Slicer& slicer, Array* arr); void getColumnSlicedoubleV (const Slicer& slicer, Array* arr); void getColumnSliceComplexV (const Slicer& slicer, Array* arr); void getColumnSliceDComplexV (const Slicer& slicer, Array* arr); void putColumnSliceBoolV (const Slicer& slicer, const Array* dataPtr); void putColumnSliceuCharV (const Slicer& slicer, const Array* dataPtr); void putColumnSliceShortV (const Slicer& slicer, const Array* dataPtr); void putColumnSliceuShortV (const Slicer& slicer, const Array* dataPtr); void putColumnSliceIntV (const Slicer& slicer, const Array* dataPtr); void putColumnSliceuIntV (const Slicer& slicer, const Array* dataPtr); void putColumnSlicefloatV (const Slicer& slicer, const Array* dataPtr); void putColumnSlicedoubleV (const Slicer& slicer, const Array* dataPtr); void putColumnSliceComplexV (const Slicer& slicer, const Array* dataPtr); void putColumnSliceDComplexV (const Slicer& slicer, 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 implementation throws an "invalid operation exception". // 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 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); // // 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 implementation throws an "invalid operation exception". // 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 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); // // 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; // Forbid copy constructor. TSMDataColumn (const TSMDataColumn&); // Forbid assignment. TSMDataColumn& operator= (const TSMDataColumn&); // 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 (uInt rownr, const void* dataPtr, Bool writeFlag); // Read or write a slice of a data cell in the cube. void accessCellSlice (uInt 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-2.4.1/tables/DataMan/TSMFile.cc000066400000000000000000000077551321422335000176400ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# Includes #include #include #include #include #include #include #include #include // for sprintf namespace casacore { //# NAMESPACE CASACORE - BEGIN TSMFile::TSMFile (const TiledStMan* stman, uInt fileSequenceNr, const TSMOption& tsmOpt, MultiFileBase* mfile) : fileSeqnr_p (fileSequenceNr), file_p (0), length_p (0) { // Create the file. char strc[8]; sprintf (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, MultiFileBase* 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, MultiFileBase* mfile) : file_p (0) { getObject (ios); if (seqnr != fileSeqnr_p) { throw DataManInternalError ("TSMFile::TSMFile " + stman->dataManagerName()); } char strc[8]; sprintf (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-2.4.1/tables/DataMan/TSMFile.h000066400000000000000000000107121321422335000174650ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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&, MultiFileBase* mfile=0); // Create a TSMFile object for the given existing file. TSMFile (const String& fileName, Bool writable, const TSMOption&, MultiFileBase* mfile=0); // 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&, MultiFileBase* mfile=0); // The destructor closes the file. ~TSMFile(); // 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; // Forbid copy constructor. TSMFile (const TSMFile&); // Forbid assignment. TSMFile& operator= (const TSMFile&); }; 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-2.4.1/tables/DataMan/TSMIdColumn.cc000066400000000000000000000061341321422335000204610ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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::getfloatV (uInt rownr, float* dataPtr) { // Get the hypercube the row is in. TSMCube* hypercube = stmanPtr_p->getHypercube (rownr); hypercube->valueRecord().get (columnName(), *dataPtr); } void TSMIdColumn::putfloatV (uInt rownr, const float* dataPtr) { float value; TSMIdColumn::getfloatV (rownr, &value); if (value != *dataPtr) { throw TSMError ("TSMIdColumn::put: new value mismatches existing " "in id column " + columnName()); } } #define TSMIDCOLUMN_GETPUT(T,NM) \ void TSMIdColumn::aips_name2(get,NM) (uInt rownr, T* dataPtr) \ { \ TSMCube* hypercube = stmanPtr_p->getHypercube (rownr); \ hypercube->valueRecord().get (columnName(), *dataPtr); \ } \ void TSMIdColumn::aips_name2(put,NM) (uInt rownr, const T* dataPtr) \ { \ T value; \ TSMIdColumn::aips_name2(get,NM) (rownr, &value); \ if (value != *dataPtr) { \ throw TSMError ("TSMIdColumn::put: new value mismatches existing" \ " in id column " + columnName()); \ } \ } TSMIDCOLUMN_GETPUT(Bool,BoolV) TSMIDCOLUMN_GETPUT(Int,IntV) TSMIDCOLUMN_GETPUT(uInt,uIntV) //#TSMIDCOLUMN_GETPUT(float,floatV) TSMIDCOLUMN_GETPUT(double,doubleV) TSMIDCOLUMN_GETPUT(Complex,ComplexV) TSMIDCOLUMN_GETPUT(DComplex,DComplexV) TSMIDCOLUMN_GETPUT(String,StringV) } //# NAMESPACE CASACORE - END casacore-2.4.1/tables/DataMan/TSMIdColumn.h000066400000000000000000000114741321422335000203260ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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(); // 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). // void getBoolV (uInt rownr, Bool* dataPtr); void getIntV (uInt rownr, Int* dataPtr); void getuIntV (uInt rownr, uInt* dataPtr); void getfloatV (uInt rownr, float* dataPtr); void getdoubleV (uInt rownr, double* dataPtr); void getComplexV (uInt rownr, Complex* dataPtr); void getDComplexV (uInt rownr, DComplex* dataPtr); void getStringV (uInt 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. // void putBoolV (uInt rownr, const Bool* dataPtr); void putIntV (uInt rownr, const Int* dataPtr); void putuIntV (uInt rownr, const uInt* dataPtr); void putfloatV (uInt rownr, const float* dataPtr); void putdoubleV (uInt rownr, const double* dataPtr); void putComplexV (uInt rownr, const Complex* dataPtr); void putDComplexV (uInt rownr, const DComplex* dataPtr); void putStringV (uInt rownr, const String* dataPtr); // private: // Forbid copy constructor. TSMIdColumn (const TSMIdColumn&); // Forbid assignment. TSMIdColumn& operator= (const TSMIdColumn&); }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/tables/DataMan/TSMOption.cc000066400000000000000000000057141321422335000202220ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/tables/DataMan/TSMOption.h000066400000000000000000000135731321422335000200660ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 MB // 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. 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. -1 means undefined. Int maxCacheSizeMB() const { return itsMaxCacheSize; } private: Option itsOption; Int itsBufferSize; Int itsMaxCacheSize; }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/tables/DataMan/TSMShape.cc000066400000000000000000000104661321422335000200120ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/tables/DataMan/TiledCellStMan.cc000066400000000000000000000153621321422335000211720ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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, uInt 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.asInt ("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 (uInt, 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::create (uInt 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. addRow (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 (uInt 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::addRow (uInt nrow) { // Resize block when needed. uInt 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 (uInt i=nrrow_p; i 0) { hypercube->setShape (fixedCellShape_p, defaultTileShape_p); } } nrrow_p += nrow; setDataChanged(); } TSMCube* TiledCellStMan::getHypercube (uInt 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 (uInt 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-2.4.1/tables/DataMan/TiledCellStMan.h000066400000000000000000000224621321422335000210330ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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. // uInt i; // for (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, uInt maximumCacheSize = 0); TiledCellStMan (const String& hypercolumnName, const Record& spec); // ~TiledCellStMan(); // 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 (uInt 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(); // Forbid copy constructor. TiledCellStMan (const TiledCellStMan&); // Forbid assignment. TiledCellStMan& operator= (const TiledCellStMan&); // Get the default tile shape. virtual IPosition defaultTileShape() const; // Add rows to the storage manager. void addRow (uInt nrrow); // Get the hypercube in which the given row is stored. virtual TSMCube* getHypercube (uInt 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 (uInt 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 create (uInt nrrow); // Read the header info. virtual void readHeader (uInt nrrow, Bool firstTime); //# Declare the data members. IPosition defaultTileShape_p; }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/tables/DataMan/TiledColumnStMan.cc000066400000000000000000000162571321422335000215540ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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, uInt 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.asInt ("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 (Bool& reask) const { reask = False; return True; } void TiledColumnStMan::create (uInt 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. addRow (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 (uInt 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::addRow (uInt nrow) { cubeSet_p[0]->extend (nrow, emptyRecord, coordColSet_p[nrdim_p - 1]); nrrow_p += nrow; setDataChanged(); } TSMCube* TiledColumnStMan::getHypercube (uInt 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 (uInt 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-2.4.1/tables/DataMan/TiledColumnStMan.h000066400000000000000000000216261321422335000214120ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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. // uInt i; // for (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, uInt maximumCacheSize = 0); TiledColumnStMan (const String& hypercolumnName, const Record& spec); // ~TiledColumnStMan(); // Clone this object. // It does not clone TSMColumn objects possibly used. virtual DataManager* clone() const; // TiledColumnStMan can always access a column. virtual Bool canAccessColumn (Bool& reask) 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(); // Forbid copy constructor. TiledColumnStMan (const TiledColumnStMan&); // Forbid assignment. TiledColumnStMan& operator= (const TiledColumnStMan&); // Get the (default) tile shape. virtual IPosition defaultTileShape() const; // Add rows to the storage manager. // This will extend the hypercube. void addRow (uInt nrrow); // Get the hypercube in which the given row is stored. virtual TSMCube* getHypercube (uInt 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 (uInt 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 create (uInt nrrow); // Read the header info. virtual void readHeader (uInt nrrow, Bool firstTime); //# Declare data members. IPosition tileShape_p; }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/tables/DataMan/TiledDataStMan.cc000066400000000000000000000203541321422335000211610ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN TiledDataStMan::TiledDataStMan () : TiledStMan (), nrUsedRowMap_p (0), nrrowLast_p (0) {} TiledDataStMan::TiledDataStMan (const String& hypercolumnName, uInt maximumCacheSize) : TiledStMan (hypercolumnName, maximumCacheSize), nrUsedRowMap_p (0), nrrowLast_p (0) {} TiledDataStMan::TiledDataStMan (const String& hypercolumnName, const Record& spec) : TiledStMan (hypercolumnName, 0), nrUsedRowMap_p (0), nrrowLast_p (0) { if (spec.isDefined ("MAXIMUMCACHESIZE")) { setPersMaxCacheSize (spec.asInt ("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::create (uInt nrrow) { // Set up the various things. setup(-1); // Add the rows for the given number of rows. addRow (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", 1); // Let the base class write its data. headerFilePut (*headerFile, cubeSet_p.nelements()); // Write the data from this object. *headerFile << nrrowLast_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 TiledDataStMan::readHeader (uInt tabNrrow, Bool firstTime) { // Open the header file and read data from it. AipsIO* headerFile = headerFileOpen(); headerFile->getstart ("TiledDataStMan"); // Let the base class read and initialize its data. headerFileGet (*headerFile, tabNrrow, firstTime, -1); // Read the data for this object. *headerFile >> nrrowLast_p; *headerFile >> nrUsedRowMap_p; getBlock (*headerFile, rowMap_p); getBlock (*headerFile, cubeMap_p); getBlock (*headerFile, posMap_p); headerFile->getend(); headerFileClose (headerFile); } void TiledDataStMan::addRow (uInt nrow) { nrrow_p += nrow; setDataChanged(); } void TiledDataStMan::checkNrrow (const IPosition& cubeShape, uInt incrInLastDim) const { uInt 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 (uInt 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, uInt incrInLastDim) { if (incrInLastDim == 0) { return; } // Extend the maps when needed. if (nrUsedRowMap_p == rowMap_p.nelements()) { rowMap_p.resize (nrUsedRowMap_p + 64); cubeMap_p.resize (nrUsedRowMap_p + 64); posMap_p.resize (nrUsedRowMap_p + 64); } // Determine the numbers of rows added via the extension. const IPosition& shape = cubeSet_p[cubeNr]->cubeShape(); rowMap_p[nrUsedRowMap_p] = nrrowLast_p; cubeMap_p[nrUsedRowMap_p] = cubeNr; posMap_p[nrUsedRowMap_p] = shape(nrdim_p-1) - incrInLastDim; nrUsedRowMap_p++; // Now update the last row number used with the // number of rows this extension represents. uInt nr = addedNrrow (cubeSet_p[cubeNr]->cubeShape(), incrInLastDim); nrrowLast_p += nr; } TSMCube* TiledDataStMan::getHypercube (uInt rownr) { IPosition pos; return TiledDataStMan::getHypercube (rownr, pos); } TSMCube* TiledDataStMan::getHypercube (uInt 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, nrUsedRowMap_p); 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]]; uInt 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 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)); // uInt i; // indgen (array); // // Write some data into the data columns. // for (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 (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. // uInt i, j; // uInt rownr = 0; // for (i=0; i<42; i++) { // if (i%2 == 0) { // table.addRow (30); // accessor.extendHypercube (1, hyperDefLine); // time.put (rownr, float(i)); // for (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 (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, uInt maximumCacheSize = 0); TiledDataStMan (const String& hypercolumnName, const Record& spec); // ~TiledDataStMan(); // 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(); // Forbid copy constructor. TiledDataStMan (const TiledDataStMan&); // Forbid assignment. TiledDataStMan& operator= (const 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 addRow (uInt 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 (uInt incrInLastDim, const Record& values); // Get the hypercube in which the given row is stored. virtual TSMCube* getHypercube (uInt 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 (uInt 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 create (uInt nrrow); // Read the header info. virtual void readHeader (uInt nrrow, Bool firstTime); // Update the map of row numbers to cube number plus offset. void updateRowMap (uInt cubeNr, uInt incrInLastDim); // Check if the table is large enough to hold this // hypercube extension. void checkNrrow (const IPosition& cubeShape, uInt incrInLastDim) const; //# Declare the data members. // 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 row number since the last hypercube extension. uInt nrrowLast_p; }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/tables/DataMan/TiledDataStManAccessor.cc000066400000000000000000000062701321422335000226450ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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-2.4.1/tables/DataMan/TiledDataStManAccessor.h000066400000000000000000000130711321422335000225040ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/tables/DataMan/TiledFileAccess.cc000066400000000000000000000417401321422335000213500ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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); uInt n = arr.nelements(); if (examineForDeleteValues) { for (uInt 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); uInt n = arr.nelements(); if (examineForDeleteValues) { for (uInt 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); uInt n = arr.nelements(); if (examineForDeleteValues) { for (uInt 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 (uInt nbytes) { itsTSM->setMaximumCacheSize (nbytes); } uInt 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-2.4.1/tables/DataMan/TiledFileAccess.h000066400000000000000000000220121321422335000212010ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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(); // 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 (uInt nbytes); // Get the maximum cache size (in bytes). uInt 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: // Forbid copy constructor and assignment. // TiledFileAccess (const TiledFileAccess&); TiledFileAccess& operator= (const TiledFileAccess&); // TSMCube* itsCube; TiledFileHelper* itsTSM; uInt itsLocalPixelSize; Bool itsWritable; DataType itsDataType; }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/tables/DataMan/TiledFileHelper.cc000066400000000000000000000103671321422335000213670ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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::create (uInt) { throw AipsError ("TileFileHelper::create - not implemented"); } TSMCube* TiledFileHelper::getHypercube (uInt) { throw AipsError ("TileFileHelper::getHypercube - not implemented"); return 0; } TSMCube* TiledFileHelper::getHypercube (uInt, IPosition&) { throw AipsError ("TileFileHelper:getHypercube: - not implemented"); return 0; } void TiledFileHelper::readHeader (uInt, Bool) { throw AipsError ("TileFileHelper::readHeader - not implemented"); } } //# NAMESPACE CASACORE - END casacore-2.4.1/tables/DataMan/TiledFileHelper.h000066400000000000000000000067731321422335000212370ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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(); 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 create (uInt); virtual TSMCube* getHypercube (uInt); virtual TSMCube* getHypercube (uInt, IPosition&); virtual void readHeader (uInt, Bool); // private: // Forbid copy constructor and assignment. // TiledFileHelper (const TiledFileHelper&); TiledFileHelper& operator= (const TiledFileHelper&); // TableDesc itsDesc; }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/tables/DataMan/TiledShapeStMan.cc000066400000000000000000000407401321422335000213510ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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, uInt 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.asInt ("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 (Bool& reask) 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. reask = True; 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 (uInt rownr, TSMCube*, const IPosition& shape, const IPosition& tileShape) { IPosition cubeShape = shape; uInt 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. uInt n = cubeSet_p.nelements(); for (uInt i=1; icubeShape(), 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("ID columns cannot be used with TiledShapeStMan"); } } void TiledShapeStMan::create (uInt 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. addRow (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 (uInt 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::addRow (uInt nrow) { uInt oldnrrow = nrrow_p; nrrow_p += nrow; if (fixedCellShape_p.nelements() > 0) { for (uInt 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 (uInt rownr, uInt cubeNr) { TSMCube* hypercube = cubeSet_p[cubeNr]; uInt 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, uInt 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. uInt 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. uInt 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 (uInt 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 (uInt 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-2.4.1/tables/DataMan/TiledShapeStMan.h000066400000000000000000000316741321422335000212210ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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. // uInt i; // for (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, uInt maximumCacheSize = 0); TiledShapeStMan (const String& hypercolumnName, const Record& spec); // ~TiledShapeStMan(); // 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. // reask is set to True (because next time things might be different). virtual Bool canAccessColumn (Bool& reask) 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 (uInt 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(); // Forbid copy constructor. TiledShapeStMan (const TiledShapeStMan&); // Forbid assignment. TiledShapeStMan& operator= (const TiledShapeStMan&); // Get the default tile shape. virtual IPosition defaultTileShape() const; // Add rows to the storage manager. void addRow (uInt 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 (uInt 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 (uInt rownr, uInt cubeNr); // Get the hypercube in which the given row is stored. virtual TSMCube* getHypercube (uInt 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 (uInt 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 create (uInt nrrow); // Read the header info. virtual void readHeader (uInt nrrow, Bool firstTime); // Update the map of row numbers to cube number plus offset. void updateRowMap (uInt cubeNr, uInt pos, uInt rownr); // Extend the map of row numbers to cube number plus offset // will new empty entries. void extendRowMap (uInt 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-2.4.1/tables/DataMan/TiledStMan.cc000066400000000000000000001054301321422335000203660ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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, uInt 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)) { Int 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, uInt(0)); PtrBlock*> values(nrdim); for (i=0; i (maxShape(i) - minShape(i) + 1); // First find exactly fitting shapes. for (Int 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 (Int 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; 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 ("ActualMaxCacheSize", Int(maxCacheSize_p)); return rec; } void TiledStMan::setProperties (const Record& rec) { if (rec.isDefined("ActualMaxCacheSize")) { setMaximumCacheSize (rec.asInt("ActualCacheSize")); } } void TiledStMan::setShape (uInt, 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 nbytes) { maxCacheSize_p = nbytes; } Bool TiledStMan::canChangeShape() const { return False; } Bool TiledStMan::canAccessColumn (Bool& reask) const { reask = True; 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, tsmOption().bufferSize()); } 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 (uInt rownr) const { return getHypercube(rownr)->cubeShape(); } const IPosition& TiledStMan::tileShape (uInt rownr) const { return getHypercube(rownr)->tileShape(); } uInt TiledStMan::bucketSize (uInt rownr) const { return getHypercube(rownr)->bucketSize(); } uInt TiledStMan::cacheSize (uInt rownr) const { return getHypercube(rownr)->cacheSize(); } uInt TiledStMan::calcCacheSize (uInt 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 (uInt 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 (uInt 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 (uInt 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]; } uInt TiledStMan::getLengthOffset (uInt nrPixels, Block& dataOffset, Block& localOffset, uInt& localTileLength) const { localTileLength = 0; uInt 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 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)); } } } } } uInt TiledStMan::addedNrrow (const IPosition& shape, uInt incrInLastDim) const { uInt 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, uInt nrCube) { uInt i; // 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()) { 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(); headerFile << nrrow_p; headerFile << ncolumn(); for (i=0; idataType(); } headerFile << hypercolumnName_p; headerFile << persMaxCacheSize_p; headerFile << nrdim_p; headerFile << uInt(fileSet_p.nelements()); for (i=0; iputObject (headerFile); } } headerFile << nrCube; for (i=0; iputObject (headerFile); } headerFile.putend(); } void TiledStMan::headerFileGet (AipsIO& headerFile, uInt tabNrrow, Bool firstTime, Int extraNdim) { nrrow_p = tabNrrow; uInt i; 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. uInt nrrow, nrcol, seqnr; int dtype; headerFile >> seqnr; headerFile >> nrrow; 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 (i=0; i> dtype; if (dtype != colSet_p[i]->dataType()) { throw (DataManInternalError ("TiledStMan::headerFileGet: mismatch in data type")); } } headerFile >> hypercolumnName_p; headerFile >> persMaxCacheSize_p; 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")); } uInt nrFile; Bool flag; headerFile >> nrFile; uInt nrold; nrold = fileSet_p.nelements(); fileSet_p.resize (nrFile); for (i=nrold; 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; } } uInt nrCube; headerFile >> nrCube; nrold = cubeSet_p.nelements(); cubeSet_p.resize (nrCube); for (i=nrold; 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]); } } 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-2.4.1/tables/DataMan/TiledStMan.h000066400000000000000000000532331321422335000202330ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef TABLES_TILEDSTMAN_H #define TABLES_TILEDSTMAN_H //# Includes #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; template class Vector; // // 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 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 maximumCacheSize); virtual ~TiledStMan(); // 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 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 ActualCacheSize 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, uInt maxNrPixelsPerTile = 32768); static IPosition makeTileShape (const IPosition& hypercubeShape, const Vector& weight, const Vector& tolerance, uInt maxNrPixelsPerTile = 32768); // // Set the maximum cache size (in bytes) in a non-persistent way. virtual void setMaximumCacheSize (uInt nbytes); // Get the current maximum cache size (in bytes). uInt maximumCacheSize() const; // Get the current cache size (in buckets) for the hypercube in // the given row. uInt cacheSize (uInt rownr) const; // Get the hypercube shape of the data in the given row. const IPosition& hypercubeShape (uInt rownr) const; // Get the tile shape of the data in the given row. const IPosition& tileShape (uInt rownr) const; // Get the bucket size (in bytes) of the hypercube in the given row. uInt bucketSize (uInt 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. // reask is set to True (because next time things might be different). virtual Bool canAccessColumn (Bool& reask) 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 (uInt 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 (uInt 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 (uInt 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 (uInt 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. uInt getLengthOffset (uInt 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. uInt 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 (uInt rownr) const; virtual TSMCube* getHypercube (uInt 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 (uInt 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 void open (uInt nrrow, AipsIO&); // Resync the storage manager with the new file contents. virtual void resync (uInt 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 (uInt 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. void setPersMaxCacheSize (uInt nbytes); // 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. uInt 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 (uInt 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, uInt 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). void headerFileGet (AipsIO& headerFile, uInt 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. uInt 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 for a hypercube. uInt persMaxCacheSize_p; // The actual maximum cache size for a hypercube. 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; private: // Forbid copy constructor. TiledStMan (const TiledStMan&); // Forbid assignment. TiledStMan& operator= (const TiledStMan&); }; inline uInt TiledStMan::maximumCacheSize() const { return maxCacheSize_p; } inline uInt TiledStMan::nrCoordVector() const { return nrCoordVector_p; } inline uInt 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 (uInt rownr) const { return const_cast(this)->getHypercube (rownr); } inline void TiledStMan::setPersMaxCacheSize (uInt nbytes) { persMaxCacheSize_p = nbytes; maxCacheSize_p = nbytes; } } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/tables/DataMan/TiledStManAccessor.cc000066400000000000000000000137271321422335000220600ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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 (uInt rownr) const { return dataManPtr_p->cacheSize (rownr); } const IPosition& ROTiledStManAccessor::hypercubeShape (uInt rownr) const { return dataManPtr_p->hypercubeShape (rownr); } const IPosition& ROTiledStManAccessor::tileShape (uInt rownr) const { return dataManPtr_p->tileShape (rownr); } uInt ROTiledStManAccessor::bucketSize (uInt rownr) const { return dataManPtr_p->bucketSize (rownr); } const Record& ROTiledStManAccessor::valueRecord (uInt 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 (uInt rownr, const IPosition& sliceShape, const IPosition& axisPath) const { return dataManPtr_p->calcCacheSize (rownr, sliceShape, IPosition(), IPosition(), axisPath); } uInt ROTiledStManAccessor::calcCacheSize (uInt 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 (uInt rownr, const IPosition& sliceShape, const IPosition& axisPath, Bool forceSmaller) { dataManPtr_p->setCacheSize (rownr, sliceShape, IPosition(), IPosition(), axisPath, forceSmaller); } void ROTiledStManAccessor::setCacheSize (uInt 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 (uInt 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-2.4.1/tables/DataMan/TiledStManAccessor.h000066400000000000000000000275561321422335000217270ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 Mb. // 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 bytes) 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 nbytes); // Get the maximum cache size (in bytes). uInt maximumCacheSize() const; // Get the current cache size (in buckets) for the hypercube in // the given row. uInt cacheSize (uInt rownr) const; // Get the hypercube shape of the data in the given row. const IPosition& hypercubeShape (uInt rownr) const; // Get the tile shape of the data in the given row. const IPosition& tileShape (uInt rownr) const; // Get the bucket size (in bytes) of the hypercube in the given row. uInt bucketSize (uInt rownr) const; // Get coordinate and id values of the hypercube in the given row. const Record& valueRecord (uInt 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 (uInt rownr, const IPosition& sliceShape, const IPosition& axisPath) const; uInt calcCacheSize (uInt 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 (uInt rownr, const IPosition& sliceShape, const IPosition& axisPath, Bool forceSmaller = True); void setCacheSize (uInt 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 (uInt 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-2.4.1/tables/DataMan/VSCEngine.h000066400000000000000000000302541321422335000200060ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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() // {} // // // 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 (uInt) // { // 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 (uInt rownr, A& value) // { // rocolx.get (rownr, value.x()); // rocoly.get (rownr, value.y()); // } // // // Put the data in a row. // virtual void put (uInt 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) // {} // // // Assignment is not needed and therefore forbidden // // (so it is made private and is not implemented). // AVSCEngine& operator= (const AVSCEngine&); // // // // 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(); // 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: // Assignment is not needed and therefore forbidden // (so it is made private). VSCEngine& operator= (const VSCEngine&); // 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-2.4.1/tables/DataMan/VSCEngine.tcc000066400000000000000000000047711321422335000203350ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/tables/DataMan/VirtArrCol.h000066400000000000000000000324501321422335000202540ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef TABLES_VIRTARRCOL_H #define TABLES_VIRTARRCOL_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations template class Array; 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 a void* 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 void* 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. For this purpose the function canAccessSlice has // also been implemented. // 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. // For this purpose the functions canAccessArrayColumn and // canAccessColumnSlice have also been implemented. //
        • // Similarly the functions get/putArrayColumnCellsV and // get/putColumnSliceCells have been templated to // get/putArrayColumnCells and get/putColumnSliceCells. // However, their implementations throw an exception and the function // canAccessArrayColumnCells has not implemented (so defaults to False). // 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. // template class VirtualArrayColumn : public DataManagerColumn { public: // Create a column. VirtualArrayColumn() {;} // Frees up the storage. virtual ~VirtualArrayColumn(); // Return the data type of the column. virtual int dataType() const; // Return the data type Id of the column. virtual String dataTypeId() const; // By default no data can be put in a virtual column. virtual Bool isWritable() const; // The class can handle a get/putSlice. virtual Bool canAccessSlice (Bool& reask) const; // The class can handle a get/putArrayColumn. virtual Bool canAccessArrayColumn (Bool& reask) const; // The class can handle a get/putColumnSlice. virtual Bool canAccessColumnSlice (Bool& reask) 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 (uInt rownr, const IPosition& shape); // Is the value shape defined in the given row? // By default it throws a "not possible" exception. virtual Bool isShapeDefined (uInt rownr); // Get the dimensionality of the item in the given row. // By default it throws a "not possible" exception. virtual uInt ndim (uInt rownr); // Get the shape of the item in the given row. // By default it throws a "not possible" exception. virtual IPosition shape (uInt rownr); // 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 (uInt 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 (uInt 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 (uInt 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 (uInt 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 (uInt rownr, void* dataPtr); // Implement the virtual functions defined in DataManagerColumn. // Put the array value into the given row. void putArrayV (uInt rownr, const void* dataPtr); // Implement the virtual functions defined in DataManagerColumn. // Get some array values in the column. void getArrayColumnCellsV (const RefRows& rownrs, void* dataPtr); // Implement the virtual functions defined in DataManagerColumn. // Put some array values in the column. void putArrayColumnCellsV (const RefRows& rownrs, const void* dataPtr); // Implement the virtual functions defined in DataManagerColumn. // Get a section of the array in the given row. void getSliceV (uInt rownr, const Slicer& slicer, void* dataPtr); // Implement the virtual functions defined in DataManagerColumn. // Put into a section of the array in the given row. void putSliceV (uInt rownr, const Slicer& slicer, const void* dataPtr); // Implement the virtual functions defined in DataManagerColumn. // Get an entire column. void getArrayColumnV (void* dataPtr); // Implement the virtual functions defined in DataManagerColumn. // Put an entire column. void putArrayColumnV (const void* dataPtr); // Implement the virtual functions defined in DataManagerColumn. // Get a section of all arrays in the column. void getColumnSliceV (const Slicer& slicer, void* dataPtr); // Implement the virtual functions defined in DataManagerColumn. // Put into section of all arrays in the column. void putColumnSliceV (const Slicer& slicer, const void* 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, void* 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 void* dataPtr); private: // The object cannot be copied. VirtualArrayColumn (const VirtualArrayColumn&); // The object cannot be assigned to. VirtualArrayColumn& operator= (const VirtualArrayColumn&); }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-2.4.1/tables/DataMan/VirtArrCol.tcc000066400000000000000000000255021321422335000205760ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 Bool VirtualArrayColumn::isWritable() const { return False; } template int VirtualArrayColumn::dataType() const { return ValType::getType (static_cast(0)); } template String VirtualArrayColumn::dataTypeId() const { return valDataTypeId (static_cast(0)); } template Bool VirtualArrayColumn::canAccessSlice (Bool& reask) const { reask = False; return True; } template Bool VirtualArrayColumn::canAccessArrayColumn (Bool& reask) const { reask = False; return True; } template Bool VirtualArrayColumn::canAccessColumnSlice (Bool& reask) const { reask = False; return True; } template void VirtualArrayColumn::getArrayV (uInt rownr, void* dataPtr) { getArray (rownr, *static_cast*>(dataPtr)); } template void VirtualArrayColumn::putArrayV (uInt rownr, const void* dataPtr) { putArray (rownr, *static_cast*>(dataPtr)); } template void VirtualArrayColumn::getSliceV (uInt rownr, const Slicer& slicer, void* dataPtr) { getSlice (rownr, slicer, *static_cast*>(dataPtr)); } template void VirtualArrayColumn::putSliceV (uInt rownr, const Slicer& slicer, const void* dataPtr) { putSlice (rownr, slicer, *static_cast*>(dataPtr)); } template void VirtualArrayColumn::getArrayColumnV (void* dataPtr) { getArrayColumn (*static_cast*>(dataPtr)); } template void VirtualArrayColumn::putArrayColumnV (const void* dataPtr) { putArrayColumn (*static_cast*>(dataPtr)); } template void VirtualArrayColumn::getArrayColumnCellsV (const RefRows& rownrs, void* dataPtr) { getArrayColumnCells (rownrs, *static_cast*>(dataPtr)); } template void VirtualArrayColumn::putArrayColumnCellsV (const RefRows& rownrs, const void* dataPtr) { putArrayColumnCells (rownrs, *static_cast*>(dataPtr)); } template void VirtualArrayColumn::getColumnSliceV (const Slicer& slicer, void* dataPtr) { getColumnSlice (slicer, *static_cast*>(dataPtr)); } template void VirtualArrayColumn::putColumnSliceV (const Slicer& slicer, const void* dataPtr) { putColumnSlice (slicer, *static_cast*>(dataPtr)); } template void VirtualArrayColumn::getColumnSliceCellsV (const RefRows& rownrs, const Slicer& slicer, void* dataPtr) { getColumnSliceCells (rownrs, slicer, *static_cast*>(dataPtr)); } template void VirtualArrayColumn::putColumnSliceCellsV (const RefRows& rownrs, const Slicer& slicer, const void* dataPtr) { putColumnSliceCells (rownrs, slicer, *static_cast*>(dataPtr)); } //# The default implementations of get/putSlice get the entire array //# and access the required slice. template void VirtualArrayColumn::getSlice (uInt rownr, const Slicer& slicer, Array& arraySlice) { //# Get the entire array and take the slice required. //# Infer the exact slice shape from the array shape. IPosition shp = shape(rownr); Array arr(shp); getArray (rownr, arr); IPosition start, end, stride; slicer.inferShapeFromSource (shp, start, end, stride); arraySlice = arr(start, end, stride); } template void VirtualArrayColumn::putSlice (uInt rownr, const Slicer& slicer, const Array& arraySlice) { //# Get the entire array, store the slice required and put //# the entire array back. //# Infer the exact slice shape from the array shape. IPosition shp = shape(rownr); Array arr(shp); getArray (rownr, arr); IPosition start, end, stride; slicer.inferShapeFromSource (shp, start, end, stride); arr(start, end, stride) = arraySlice; putArray (rownr, arr); } //# The default implementations of get/putArrayColumn handle the //# array in each individual row. template void VirtualArrayColumn::getArrayColumn (Array& array) { ArrayIterator iter(array, array.ndim()-1); uInt rownr = 0; while (! iter.pastEnd()) { getArray (rownr, iter.array()); rownr++; iter.next(); } } template void VirtualArrayColumn::putArrayColumn (const Array& array) { ReadOnlyArrayIterator iter(array, array.ndim()-1); uInt rownr = 0; while (! iter.pastEnd()) { putArray (rownr, iter.array()); rownr++; iter.next(); } } //# The default implementations of get/putColumnSlice take a slice //# for each individual row. template void VirtualArrayColumn::getColumnSlice (const Slicer& slicer, Array& array) { ArrayIterator iter(array, array.ndim()-1); uInt rownr = 0; while (! iter.pastEnd()) { getSlice (rownr, slicer, iter.array()); rownr++; iter.next(); } } template void VirtualArrayColumn::putColumnSlice (const Slicer& slicer, const Array& array) { ReadOnlyArrayIterator iter(array, array.ndim()-1); uInt rownr = 0; while (! iter.pastEnd()) { putSlice (rownr, slicer, iter.array()); rownr++; iter.next(); } } //# The default implementations of the Cells functions throw an exception. template void VirtualArrayColumn::getArrayColumnCells (const RefRows& rownrs, Array& value) { ArrayIterator iter(value, value.ndim()-1); RefRowsSliceIter rowiter(rownrs); while (! rowiter.pastEnd()) { uInt rownr = rowiter.sliceStart(); uInt end = rowiter.sliceEnd(); uInt incr = rowiter.sliceIncr(); while (rownr <= end) { if (! isFixedShape()) { if (! iter.array().shape().isEqual (shape(rownr))) { throw DataManError("getArrayColumnCells shape mismatch" " for column " + columnName()); } } getArray (rownr, iter.array()); rownr += incr; iter.next(); } rowiter++; } } template void VirtualArrayColumn::putArrayColumnCells (const RefRows& rownrs, const Array& value) { ReadOnlyArrayIterator iter(value, value.ndim()-1); RefRowsSliceIter rowiter(rownrs); while (! rowiter.pastEnd()) { uInt rownr = rowiter.sliceStart(); uInt end = rowiter.sliceEnd(); uInt incr = rowiter.sliceIncr(); while (rownr <= end) { putArray (rownr, iter.array()); rownr += incr; iter.next(); } rowiter++; } } template void VirtualArrayColumn::getColumnSliceCells (const RefRows& rownrs, const Slicer& ns, Array& value) { ArrayIterator iter(value, value.ndim()-1); RefRowsSliceIter rowiter(rownrs); while (! rowiter.pastEnd()) { uInt rownr = rowiter.sliceStart(); uInt end = rowiter.sliceEnd(); uInt incr = rowiter.sliceIncr(); while (rownr <= end) { getSlice (rownr, ns, iter.array()); rownr += incr; iter.next(); } rowiter++; } } template void VirtualArrayColumn::putColumnSliceCells (const RefRows& rownrs, const Slicer& ns, const Array& value) { ReadOnlyArrayIterator iter(value, value.ndim()-1); RefRowsSliceIter rowiter(rownrs); while (! rowiter.pastEnd()) { uInt rownr = rowiter.sliceStart(); uInt end = rowiter.sliceEnd(); uInt incr = rowiter.sliceIncr(); while (rownr <= end) { putSlice (rownr, ns, iter.array()); rownr += incr; iter.next(); } rowiter++; } } //# The default implementation of the put function throws //# an exception. template void VirtualArrayColumn::putArray (uInt, const Array&) { throw DataManInvOper ("VirtualArrayColumn::putArray not possible" " for column " + columnName()); } //# The default implementations of the shape functions throw //# an exception. template void VirtualArrayColumn::setShapeColumn (const IPosition&) { throw DataManInvOper ("VirtualArrayColumn::setShapeColumn not possible" " for column " + columnName()); } template void VirtualArrayColumn::setShape (uInt, const IPosition&) { throw DataManInvOper ("VirtualArrayColumn::setShape not possible" " for column " + columnName()); } template Bool VirtualArrayColumn::isShapeDefined (uInt) { throw DataManInvOper ("VirtualArrayColumn::isShapeDefined not possible" " for column " + columnName()); return False; } template uInt VirtualArrayColumn::ndim (uInt) { throw DataManInvOper ("VirtualArrayColumn::ndim not possible" " for column " + columnName()); return 0; } template IPosition VirtualArrayColumn::shape (uInt) { throw DataManInvOper ("VirtualArrayColumn::shape not possible" " for column " + columnName()); return IPosition(0); } } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/tables/DataMan/VirtColEng.cc000066400000000000000000000057361321422335000204060ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN VirtualColumnEngine::~VirtualColumnEngine() {} Bool VirtualColumnEngine::isStorageManager() const { return False; } Bool VirtualColumnEngine::canAddRow() const { return True; } void VirtualColumnEngine::addRow (uInt) {} Bool VirtualColumnEngine::canRemoveRow() const { return True; } void VirtualColumnEngine::removeRow (uInt) {} //# Create, open, prepare and close do nothing unless implemented in the // derived class. Bool VirtualColumnEngine::flush (AipsIO&, Bool) { return False; } void VirtualColumnEngine::create (uInt) {} void VirtualColumnEngine::open (uInt, AipsIO&) {} void VirtualColumnEngine::resync (uInt) {} 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-2.4.1/tables/DataMan/VirtColEng.h000066400000000000000000000221041321422335000202340ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 ScaledArray. // // // 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(); private: // The copy constructor cannot be used for this base class. // The clone function should be used instead. // The private declaration of this constructor makes it unusable. VirtualColumnEngine (const VirtualColumnEngine& that); // Assignment cannot be used for this base class. // The private declaration of this operator makes it unusable. VirtualColumnEngine& operator= (const VirtualColumnEngine&); // 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 addRow (uInt nrrow); // Delete a row from all columns. // The default implementation does nothing. virtual void removeRow (uInt 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 void resync (uInt 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 create (uInt 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 void open (uInt 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-2.4.1/tables/DataMan/VirtScaCol.h000066400000000000000000000247301321422335000202400ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef TABLES_VIRTSCACOL_H #define TABLES_VIRTSCACOL_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations template class Vector; // // 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. // template class VirtualScalarColumn : public DataManagerColumn { public: // Create a column. VirtualScalarColumn() {;} // Frees up the storage. virtual ~VirtualScalarColumn(); // Return the data type of the column. int dataType() const; // Return the data type Id of the column. String dataTypeId() const; // By default no data can be put in a virtual column. virtual Bool isWritable() const; // Get the scalar value in the given row. virtual void get (uInt rownr, T& data) = 0; // Put the scalar value into the given row. // The default implementation throws an exception. virtual void put (uInt rownr, const T& data); protected: // The class can handle a get/putScalarColumn. Bool canAccessScalarColumn (Bool& reask) const; // Get all scalar values in the column. // The argument dataPtr is in fact a Vector*, but a void* // 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 getScalarColumn (Vector& data); // Put all scalar values in the column. // The argument dataPtr is in fact a const Vector*, but a const void* // 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). virtual void putScalarColumn (const Vector& data); // Get scalars from the given row on with a maximum of nrmax values. // It returns the actual number of values got. // This can be used to get an entire column of scalars or to get // a part of a column (for a cache for example). // The argument dataPtr is in fact a T*, but a void* // is needed to be generic. It must have length nrmax. virtual uInt getBlock (uInt rownr, uInt nrmax, T* dataPtr); // Put nrmax scalars from the given row on. // It returns the actual number of values put. // This can be used to put an entire column of scalars or to put // a part of a column (for a cache for example). // The argument dataPtr is in fact a const T*, but a const void* // is needed to be generic. It must have length nrmax. virtual void putBlock (uInt rownr, uInt nrmax, const T* dataPtr); private: // Implement the virtual functions defined in DataManagerColumn. // Get the scalar value in the given row. // void getBoolV (uInt rownr, Bool* dataPtr); void getuCharV (uInt rownr, uChar* dataPtr); void getShortV (uInt rownr, Short* dataPtr); void getuShortV (uInt rownr, uShort* dataPtr); void getIntV (uInt rownr, Int* dataPtr); void getuIntV (uInt rownr, uInt* dataPtr); void getfloatV (uInt rownr, float* dataPtr); void getdoubleV (uInt rownr, double* dataPtr); void getComplexV (uInt rownr, Complex* dataPtr); void getDComplexV (uInt rownr, DComplex* dataPtr); void getStringV (uInt rownr, String* dataPtr); // This function is the get for all non-standard data types. void getOtherV (uInt rownr, void* dataPtr); // // Implement the virtual functions defined in DataManagerColumn. // Put the scalar value into the given row. // void putBoolV (uInt rownr, const Bool* dataPtr); void putuCharV (uInt rownr, const uChar* dataPtr); void putShortV (uInt rownr, const Short* dataPtr); void putuShortV (uInt rownr, const uShort* dataPtr); void putIntV (uInt rownr, const Int* dataPtr); void putuIntV (uInt rownr, const uInt* dataPtr); void putfloatV (uInt rownr, const float* dataPtr); void putdoubleV (uInt rownr, const double* dataPtr); void putComplexV (uInt rownr, const Complex* dataPtr); void putDComplexV (uInt rownr, const DComplex* dataPtr); void putStringV (uInt rownr, const String* dataPtr); // This function is the put for all non-standard data types. void putOtherV (uInt rownr, const void* dataPtr); // // Implement the virtual functions defined in DataManagerColumn. // Get all scalar values in the column. void getScalarColumnV (void* dataPtr); // Implement the virtual functions defined in DataManagerColumn. // Put all scalar values in the column. void putScalarColumnV (const void* dataPtr); // Implement the virtual functions defined in DataManagerColumn. // Get scalars from the given row on with a maximum of nrmax values. uInt getBlockV (uInt rownr, uInt nrmax, void* dataPtr); // Implement the virtual functions defined in DataManagerColumn. // Put nrmax scalars from the given row on. void putBlockV (uInt rownr, uInt nrmax, const void* dataPtr); private: // The object cannot be copied. VirtualScalarColumn (const VirtualScalarColumn&); // The object cannot be assigned to. VirtualScalarColumn& operator= (const VirtualScalarColumn&); }; // // Global functions to get or put data of a virtual column // // // // template inline void getVirtualScalarColumn (VirtualScalarColumn* col, uInt rownr, T* dataPtr) { col->get (rownr, *dataPtr); } inline void getVirtualScalarColumn (DataManagerColumn* col, uInt, void*) { col->throwGet(); } template inline void putVirtualScalarColumn (VirtualScalarColumn* col, uInt rownr, const T* dataPtr) { col->put (rownr, *dataPtr); } inline void putVirtualScalarColumn (DataManagerColumn* col, uInt, const void*) { col->throwPut(); } // } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-2.4.1/tables/DataMan/VirtScaCol.tcc000066400000000000000000000125471321422335000205650ustar00rootroot00000000000000//# VirtScaCol.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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef TABLES_VIRTSCACOL_TCC #define TABLES_VIRTSCACOL_TCC //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template VirtualScalarColumn::~VirtualScalarColumn() {} template Bool VirtualScalarColumn::isWritable() const { return False; } template int VirtualScalarColumn::dataType() const { return ValType::getType (static_cast(0)); } template String VirtualScalarColumn::dataTypeId() const { return valDataTypeId (static_cast(0)); } template Bool VirtualScalarColumn::canAccessScalarColumn (Bool& reask) const { reask = False; return True; } //# Implement the get/put functions via a macro. //# In principle they are not possible. //# The implementation is done using global functions iso. specializations //# to overcome the limitations of the SGI compiler (in May 1995). #define VIRTUALSCALARCOLUMN_GETPUT(TP,NM) \ template \ void VirtualScalarColumn::aips_name2(get,NM) (uInt rownr, TP* dataPtr) \ { getVirtualScalarColumn (this, rownr, dataPtr); } \ template \ void VirtualScalarColumn::aips_name2(put,NM) (uInt rownr, \ const TP* dataPtr) \ { putVirtualScalarColumn (this, rownr, dataPtr); } VIRTUALSCALARCOLUMN_GETPUT(Bool,BoolV) VIRTUALSCALARCOLUMN_GETPUT(uChar,uCharV) VIRTUALSCALARCOLUMN_GETPUT(Short,ShortV) VIRTUALSCALARCOLUMN_GETPUT(uShort,uShortV) VIRTUALSCALARCOLUMN_GETPUT(Int,IntV) VIRTUALSCALARCOLUMN_GETPUT(uInt,uIntV) VIRTUALSCALARCOLUMN_GETPUT(float,floatV) VIRTUALSCALARCOLUMN_GETPUT(double,doubleV) VIRTUALSCALARCOLUMN_GETPUT(Complex,ComplexV) VIRTUALSCALARCOLUMN_GETPUT(DComplex,DComplexV) VIRTUALSCALARCOLUMN_GETPUT(String,StringV) template void VirtualScalarColumn::getOtherV (uInt rownr, void* dataPtr) { get (rownr, *(T*)dataPtr); } template void VirtualScalarColumn::putOtherV (uInt rownr, const void* dataPtr) { put (rownr, *(const T*)dataPtr); } //# Implement the generic functions as typed functions. template void VirtualScalarColumn::getScalarColumnV (void* dataPtr) { getScalarColumn (*(Vector*)dataPtr); } template void VirtualScalarColumn::putScalarColumnV (const void* dataPtr) { putScalarColumn (*(const Vector*)dataPtr); } template uInt VirtualScalarColumn::getBlockV (uInt rownr, uInt nrmax, void* dataPtr) { return getBlock (rownr, nrmax, (T*)dataPtr); } template void VirtualScalarColumn::putBlockV (uInt rownr, uInt nrmax, const void* dataPtr) { putBlock (rownr, nrmax, (const T*)dataPtr); } //# Now implement the default implementations of the typed functions. //# The default implementation of put throws an exception. template void VirtualScalarColumn::put (uInt, const T&) { throwPut(); } //# The default implementation of get/putScalarColumn handles its data using //# get/putBlock. template void VirtualScalarColumn::getScalarColumn (Vector& vec) { Bool deleteIt; T* data = vec.getStorage (deleteIt); uInt nrgot; uInt rownr=0; for (uInt nrtodo=vec.nelements(); nrtodo>0;) { nrgot = getBlock (rownr, nrtodo, data); nrtodo -= nrgot; data += nrgot; rownr += nrgot; } vec.putStorage (data, deleteIt); } template void VirtualScalarColumn::putScalarColumn (const Vector& vec) { Bool deleteIt; const T* data =vec.getStorage (deleteIt); putBlock (0, vec.nelements(), data); vec.freeStorage (data, deleteIt); } //# The default implementation of getBlock gets one value. //# The default implementation of putBlock puts one value at a time. template uInt VirtualScalarColumn::getBlock (uInt rownr, uInt nrmax, T* dataPtr) { if (nrmax > 0) { get (rownr, *dataPtr); return 1; } return 0; } template void VirtualScalarColumn::putBlock (uInt rownr, uInt nrmax, const T* dataPtr) { while (nrmax > 0) { put (rownr++, *dataPtr++); nrmax--; } } } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/tables/DataMan/VirtualTaQLColumn.cc000066400000000000000000000306011321422335000217050ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: VirtualTaQLColumn.cc 21521 2014-12-10 08:06:42Z gervandiepen $ #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN VirtualTaQLColumn::VirtualTaQLColumn (const String& expr) : itsDataType (TpOther), itsIsArray (False), itsExpr (expr), itsNode (0), itsTempWritable (False), itsCurRow (-1), itsCurResult (0) {} VirtualTaQLColumn::VirtualTaQLColumn (const Record& spec) : itsDataType (TpOther), itsIsArray (False), itsNode (0), itsTempWritable (False), itsCurRow (-1), itsCurResult (0) { if (spec.isDefined ("TAQLCALCEXPR")) { itsExpr = spec.asString ("TAQLCALCEXPR"); } } VirtualTaQLColumn::~VirtualTaQLColumn() { if (itsCurResult != 0) { clearCurResult(); } delete itsNode; } void VirtualTaQLColumn::clearCurResult() { switch (itsDataType) { case TpBool: delete static_cast*>(itsCurResult); break; case TpUChar: delete static_cast*>(itsCurResult); break; case TpShort: delete static_cast*>(itsCurResult); break; case TpUShort: delete static_cast*>(itsCurResult); break; case TpInt: delete static_cast*>(itsCurResult); break; case TpUInt: delete static_cast*>(itsCurResult); break; case TpFloat: delete static_cast*>(itsCurResult); break; case TpDouble: delete static_cast*>(itsCurResult); break; case TpComplex: delete static_cast*>(itsCurResult); break; case TpDComplex: delete static_cast*>(itsCurResult); break; case TpString: delete static_cast*>(itsCurResult); break; default: throw DataManError ("VirtualTaQLColumn::clearResult - unknown data type"); } itsCurResult = 0; itsCurRow = -1; } DataManager* VirtualTaQLColumn::clone() const { DataManager* dmPtr = new VirtualTaQLColumn (itsExpr); 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::create (uInt) { // Define a keyword in the column telling the expression. itsTempWritable = True; TableColumn tabcol (table(), itsColumnName); itsTempWritable = False; tabcol.rwKeywordSet().define ("_VirtualTaQLEngine_CalcExpr", itsExpr); } void VirtualTaQLColumn::prepare() { // Get the expression. TableColumn tabcol (table(), itsColumnName); itsExpr = tabcol.keywordSet().asString ("_VirtualTaQLEngine_CalcExpr"); // Compile the expression. TaQLResult res = tableCommand ("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 = TpInt; break; case TpFloat: exptype = TpDouble; break; default: break; } if (itsNode->dataType() != exptype) { throw DataManError ("VirtualTaQLColumn: " "expression and column " + itsColumnName + " data type mismatch"); } } 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; } int VirtualTaQLColumn::dataType() const { return itsDataType; } Bool VirtualTaQLColumn::isWritable() const { return itsTempWritable; } uInt VirtualTaQLColumn::ndim (uInt rownr) { return shape(rownr).nelements(); } IPosition VirtualTaQLColumn::shape (uInt rownr) { if (!itsIsArray) { return IPosition(); } IPosition shp = itsNode->getNodeRep()->shape(); if (shp.nelements() > 0) { return shp; } if (Int(rownr) != itsCurRow) { itsCurShape = getResult (rownr, itsCurResult); itsCurRow = rownr; } return itsCurShape; } Bool VirtualTaQLColumn::isShapeDefined (uInt) { return True; } void VirtualTaQLColumn::getBoolV (uInt rownr, Bool* dataPtr) { *dataPtr = itsNode->getBool (rownr); } void VirtualTaQLColumn::getuCharV (uInt rownr, uChar* dataPtr) { *dataPtr = uChar(itsNode->getInt (rownr)); } void VirtualTaQLColumn::getShortV (uInt rownr, Short* dataPtr) { *dataPtr = Short(itsNode->getInt (rownr)); } void VirtualTaQLColumn::getuShortV (uInt rownr, uShort* dataPtr) { *dataPtr = uShort(itsNode->getInt (rownr)); } void VirtualTaQLColumn::getIntV (uInt rownr, Int* dataPtr) { *dataPtr = Int(itsNode->getInt (rownr)); } void VirtualTaQLColumn::getuIntV (uInt rownr, uInt* dataPtr) { *dataPtr = uInt(itsNode->getInt (rownr)); } void VirtualTaQLColumn::getfloatV (uInt rownr, float* dataPtr) { *dataPtr = Float(itsNode->getDouble (rownr)); } void VirtualTaQLColumn::getdoubleV (uInt rownr, double* dataPtr) { *dataPtr = itsNode->getDouble (rownr); } void VirtualTaQLColumn::getComplexV (uInt rownr, Complex* dataPtr) { *dataPtr = Complex(itsNode->getDComplex (rownr)); } void VirtualTaQLColumn::getDComplexV (uInt rownr, DComplex* dataPtr) { *dataPtr = itsNode->getDComplex (rownr); } void VirtualTaQLColumn::getStringV (uInt rownr, String* dataPtr) { *dataPtr = itsNode->getString (rownr); } void VirtualTaQLColumn::getArrayV (uInt rownr, void* dataPtr) { // Usually getShape is called before getArray. // To avoid double calculation of the same value, the result is cached // by getShape in itsCurResult (by getResult). if (Int(rownr) != itsCurRow) { getResult (rownr, dataPtr); return; } switch (itsDataType) { case TpBool: *static_cast*>(dataPtr) = *static_cast*>(itsCurResult); break; case TpUChar: *static_cast*>(dataPtr) = *static_cast*>(itsCurResult); break; case TpShort: *static_cast*>(dataPtr) = *static_cast*>(itsCurResult); break; case TpUShort: *static_cast*>(dataPtr) = *static_cast*>(itsCurResult); break; case TpInt: *static_cast*>(dataPtr) = *static_cast*>(itsCurResult); break; case TpUInt: *static_cast*>(dataPtr) = *static_cast*>(itsCurResult); break; case TpFloat: *static_cast*>(dataPtr) = *static_cast*>(itsCurResult); break; case TpDouble: *static_cast*>(dataPtr) = *static_cast*>(itsCurResult); break; case TpComplex: *static_cast*>(dataPtr) = *static_cast*>(itsCurResult); break; case TpDComplex: *static_cast*>(dataPtr) = *static_cast*>(itsCurResult); break; case TpString: *static_cast*>(dataPtr) = *static_cast*>(itsCurResult); break; default: throw DataManError ("VirtualTaQLColumn::getArrayV - unknown data type"); } clearCurResult(); } IPosition VirtualTaQLColumn::getResult (uInt rownr, void* dataPtr) { IPosition shp; switch (itsDataType) { case TpBool: { Array arr =itsNode->getArrayBool (rownr); Array& out = *static_cast*>(dataPtr); out.reference (arr); shp = out.shape(); break; } case TpUChar: { Array arr = itsNode->getArrayInt (rownr); Array& out = *static_cast*>(dataPtr); out.resize (arr.shape()); convertArray (out, arr); shp = out.shape(); break; } case TpShort: { Array arr = itsNode->getArrayInt (rownr); Array& out = *static_cast*>(dataPtr); out.resize (arr.shape()); convertArray (out, arr); shp = out.shape(); break; } case TpUShort: { Array arr = itsNode->getArrayInt (rownr); Array& out = *static_cast*>(dataPtr); out.resize (arr.shape()); convertArray (out, arr); shp = out.shape(); break; } case TpInt: { Array arr = itsNode->getArrayInt (rownr); Array& out = *static_cast*>(dataPtr); out.resize (arr.shape()); convertArray (out, arr); shp = out.shape(); break; } case TpUInt: { Array arr = itsNode->getArrayInt (rownr); Array& out = *static_cast*>(dataPtr); out.resize (arr.shape()); convertArray (out, arr); shp = out.shape(); break; } case TpFloat: { Array arr = itsNode->getArrayDouble (rownr); Array& out = *static_cast*>(dataPtr); out.resize (arr.shape()); convertArray (out, arr); shp = out.shape(); break; } case TpDouble: { Array arr = itsNode->getArrayDouble (rownr); Array& out = *static_cast*>(dataPtr); out.reference (arr); shp = out.shape(); break; } case TpComplex: { Array arr = itsNode->getArrayDComplex (rownr); Array& out = *static_cast*>(dataPtr); out.resize (arr.shape()); convertArray (out, arr); shp = out.shape(); break; } case TpDComplex: { Array arr = itsNode->getArrayDComplex (rownr); Array& out = *static_cast*>(dataPtr); out.reference (arr); shp = out.shape(); break; } case TpString: { Array arr = itsNode->getArrayString (rownr); Array& out = *static_cast*>(dataPtr); out.reference (arr); shp = out.shape(); break; } default: throw DataManError ("VirtualTaQLColumn::getResult - unknown data type"); } return shp; } } //# NAMESPACE CASACORE - END casacore-2.4.1/tables/DataMan/VirtualTaQLColumn.h000066400000000000000000000176741321422335000215660ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 expression in which possibly other columns are used. // It is (of course) only possible to get data from the column; puts cannot // be done. //
        // 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. // 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); // Construct it with the given specification. VirtualTaQLColumn (const Record& spec); // Destructor is mandatory. virtual ~VirtualTaQLColumn(); // 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; } // Functions to return column info. // virtual int dataType() const; virtual Bool isWritable() const; virtual uInt ndim (uInt rownr); virtual IPosition shape (uInt rownr); virtual Bool isShapeDefined (uInt rownr); // private: // Copy is not needed and therefore forbidden (so it is made private). VirtualTaQLColumn (const VirtualTaQLColumn&); // Assignment is not needed and therefore forbidden (so it is made private). VirtualTaQLColumn& operator= (const VirtualTaQLColumn&); // 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 create (uInt); // Prepare compiles the expression. virtual void prepare(); //# We could also define the getBlockXXV 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 getColumn. // Get the scalar value in the given row. // The default implementation throws an "invalid operation" exception. // 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); // // Get the array value in the given row. // The argument dataPtr is in fact an Array*, but a void* // 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 throws an "invalid operation" exception. virtual void getArrayV (uInt rownr, void* dataPtr); // Get the result. IPosition getResult (uInt rownr, void* dataPtr); // Clear the result cache. void clearCurResult(); //# Now define the data members. int itsDataType; Bool itsIsArray; String itsColumnName; String itsExpr; //# TaQL expression TableExprNode* itsNode; //# compiled TaQL expression Bool itsTempWritable; Int itsCurRow; //# Currently evaluated row void* itsCurResult; //# result in itsCurRow IPosition itsCurShape; //# shape in itsCurRow }; } //end namespace casacore #endif casacore-2.4.1/tables/DataMan/test/000077500000000000000000000000001321422335000170275ustar00rootroot00000000000000casacore-2.4.1/tables/DataMan/test/CMakeLists.txt000066400000000000000000000015021321422335000215650ustar00rootroot00000000000000set (tests dRetypedArrayEngine dVSCEngine dVirtColEng nISMBucket tBitFlagsEngine tCompressComplex tCompressFloat tForwardCol tForwardColRow tIncrementalStMan tMappedArrayEngine tMemoryStMan tScaledArrayEngine tScaledComplexData tSSMAddRemove tSSMStringHandler tStandardStMan tStArrayFile tStMan tStMan1 tTiledBool tTiledCellStM_1 tTiledCellStMan tTiledColumnStMan tTiledDataStM_1 tTiledDataStMan tTiledEmpty tTiledFileAccess tTiledShapeStM_1 tTiledShapeStMan tTiledStMan tTSMShape tVirtColEng tVirtualTaQLColumn tVSCEngine ) # Some test sources include a test .h file. include_directories ( . ) foreach (test ${tests}) add_executable (${test} ${test}.cc) target_link_libraries (${test} casa_tables) add_test (${test} ${CMAKE_SOURCE_DIR}/cmake/cmake_assay ./${test}) add_dependencies(check ${test}) endforeach (test) casacore-2.4.1/tables/DataMan/test/dRetypedArrayEngine.cc000066400000000000000000000313321321422335000232450ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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 (AipsError x) { cout << "Caught an exception: " << x.getMesg() << 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); uInt i=0; i = 0; for (i=0; i matA = colA.getColumn(); for (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); uInt i=0; i = 0; for (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); uInt i=0; i = 0; for (i=0; i #include #include #include #include //# Forward Declarations namespace casacore { class TableRecord; template class Array; template class Vector; } // // 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) {} RetypedArrayEx1(const RetypedArrayEx1& that): x_p(that.x_p), y_p(that.y_p) {} 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-2.4.1/tables/DataMan/test/dRetypedArrayEngine.out000066400000000000000000000010341321422335000234630ustar00rootroot00000000000000get 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-2.4.1/tables/DataMan/test/dVSCEngine.cc000066400000000000000000000133761321422335000212750ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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) : VSCEngine (sourceColumnName), xTargetName_p (xTargetColumnName), yTargetName_p (yTargetColumnName) {} VSCExampleVSCEngine::VSCExampleVSCEngine (const VSCExampleVSCEngine& that) : VSCEngine (that), xTargetName_p (that.xTargetName_p), yTargetName_p (that.yTargetName_p) {} VSCExampleVSCEngine::~VSCExampleVSCEngine() {} DataManager* VSCExampleVSCEngine::clone() const { DataManager* dmPtr = new VSCExampleVSCEngine (sourceColumnName(), xTargetName_p, yTargetName_p); if (dmPtr == 0) { throw (AllocError ("VSCExampleVSCEngine::clone()", 1)); } return dmPtr; } // Store the target column names in the keywords of this column. void VSCExampleVSCEngine::create (uInt) { TableColumn src (table(), sourceColumnName()); src.rwKeywordSet().define ("_xTargetName", xTargetName_p); src.rwKeywordSet().define ("_yTargetName", yTargetName_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"); colx.attach (table(), xTargetName_p); coly.attach (table(), yTargetName_p); } void VSCExampleVSCEngine::get (uInt rownr, VSCExample& value) { colx.get (rownr, value.x()); coly.get (rownr, value.y()); } void VSCExampleVSCEngine::put (uInt rownr, const VSCExample& value) { colx.put (rownr, value.x()); coly.put (rownr, value.y()); } 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 #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 CountedPtr >; template class PtrRep >; template String valDataTypeId(VSCExample const *); } #endif casacore-2.4.1/tables/DataMan/test/dVSCEngine.h000066400000000000000000000117431321422335000211330ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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) : x_p(x), y_p(y) {} VSCExample(const VSCExample& that): x_p(that.x_p), y_p(that.y_p) {} static String dataTypeId() { return "VSCExample"; } 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 VSCExample& that) const { return x_p==that.x_p && y_p==that.y_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); // Destructor is mandatory. ~VSCExampleVSCEngine(); // Clone the object. DataManager* clone() const; // Store the target column names in the source column keywords. void create (uInt); // Prepare the engine by allocating column objects // for the target columns. void prepare(); // Get the data from a row. void get (uInt rownr, VSCExample& value); // Put the data in a row. void put (uInt 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&); // Assignment is not needed and therefore forbidden // (so it is made private). VSCExampleVSCEngine& operator= (const VSCExampleVSCEngine&); // The target column names. String xTargetName_p; String yTargetName_p; // Objects for the target columns. ScalarColumn colx; ScalarColumn coly; 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-2.4.1/tables/DataMan/test/dVirtColEng.cc000066400000000000000000000210101321422335000215100ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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::create (uInt) {} void DummyVirtualEngine::open (uInt, AipsIO& ios) { data1_p.open (ios); data2_p.open (ios); } 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 (uInt rownr, double& data) { data = scale_p * (*column_p)(rownr); } void DummyVirtualScalar::getdoubleV (uInt rownr, double* dataPtr) { *dataPtr = scale_p * (*column_p)(rownr); } void DummyVirtualScalar::put (uInt rownr, const double& data) { column_p->put (rownr, Int(data / scale_p)); } void DummyVirtualScalar::putdoubleV (uInt 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 (uInt rownr, const IPosition& shape) { column_p->setShape (rownr, shape); } Bool DummyVirtualArray::isShapeDefined (uInt rownr) { return column_p->isDefined (rownr); } uInt DummyVirtualArray::ndim (uInt rownr) { return column_p->ndim (rownr); } IPosition DummyVirtualArray::shape (uInt rownr) { return column_p->shape (rownr); } void DummyVirtualArray::getArray (uInt 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 (uInt 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-2.4.1/tables/DataMan/test/dVirtColEng.h000066400000000000000000000312341321422335000213630ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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(); // 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: // Assignment is not needed and therefore forbidden (so it is made private). DummyVirtualScalar& operator= (const DummyVirtualScalar&); // 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 (uInt rownr, double& data); // We also implement the getdoubleV, because that saves a // virtual function call. void getdoubleV (uInt rownr, double* dataPtr); //-grp // Put a value. //+grp void put (uInt rownr, const double& data); // We also implement the putdoubleV, because that saves a // virtual function call. void putdoubleV (uInt 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(); // 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: // Assignment is not needed and therefore forbidden (so it is made private). DummyVirtualArray& operator= (const DummyVirtualArray&); // 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 (uInt rownr, const IPosition& shape); // Test if the (underlying) array is defined. Bool isShapeDefined (uInt rownr); // Get the dimensionality of the (underlying) array. uInt ndim (uInt rownr); // Get the shape of the (underlying) array. IPosition shape (uInt rownr); // Get an array. void getArray (uInt rownr, Array& array); // Put an array. void putArray (uInt 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(); // 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: // The copy constructor is forbidden in this example (so it is private). // You can make it available if you want/need to. // 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&); // Assignment is forbidden in this example (so it is private). // You can make it available if you want/need to. // 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&); //# 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 create (uInt initialNrrow); // Initialize the object for an existing table with the given number // of rows. // It will read back the data written by close. void open (uInt 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-2.4.1/tables/DataMan/test/nISMBucket.cc000066400000000000000000000160511321422335000213050ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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::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 (AipsError x) { cout << "Caught an exception: " << x.getMesg() << endl; return 1; } return 0; // exit with success status } casacore-2.4.1/tables/DataMan/test/tBitFlagsEngine.out000066400000000000000000000001441321422335000225640ustar00rootroot00000000000000get 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-2.4.1/tables/DataMan/test/tCompressComplex.cc000066400000000000000000000406471321422335000226600ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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))); } //# 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 (AipsError x) { /// cout << x.getMesg() << 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 (AipsError x) { cout << "Caught an exception: " << x.getMesg() << endl; return 1; } return sts; } casacore-2.4.1/tables/DataMan/test/tCompressFloat.cc000066400000000000000000000265341321422335000223150ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { /// cout << x.getMesg() << 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 (AipsError x) { cout << "Caught an exception: " << x.getMesg() << endl; return 1; } return sts; } casacore-2.4.1/tables/DataMan/test/tForwardCol.cc000066400000000000000000000245771321422335000216030ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { cout << "Caught an exception: " << x.getMesg() << 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); sprintf (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); sprintf (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-2.4.1/tables/DataMan/test/tForwardCol.out000066400000000000000000000050631321422335000220120ustar00rootroot00000000000000Checking 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-2.4.1/tables/DataMan/test/tForwardColRow.cc000066400000000000000000000231041321422335000222540ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { cout << "Caught an exception: " << x.getMesg() << 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); sprintf (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(); int main (int argc, const char* argv[]) { 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(); } catch (AipsError x) { cout << "Caught an exception: " << x.getMesg() << 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 (AipsError x) { cout << x.getMesg() << endl; // shape cannot change } try { arr7.put (0, vecb); } catch (AipsError x) { cout << x.getMesg() << 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); } casacore-2.4.1/tables/DataMan/test/tIncrementalStMan.out000066400000000000000000002004761321422335000231610ustar00rootroot0000000000000010, 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 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 ArrayColumn::put for row 0 in column arr7: Table array conformance error #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 ArrayColumn::put for row 0 in column arr7: Table array conformance error #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 ArrayColumn::put for row 0 in column arr7: Table array conformance error #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 ArrayColumn::put for row 0 in column arr7: Table array conformance error #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 ArrayColumn::put for row 0 in column arr7: Table array conformance error #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 ArrayColumn::put for row 0 in column arr7: Table array conformance error #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-2.4.1/tables/DataMan/test/tIncrementalStMan.run000066400000000000000000000007521321422335000231510ustar00rootroot00000000000000#!/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-2.4.1/tables/DataMan/test/tMappedArrayEngine.cc000066400000000000000000000170011321422335000230540ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (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. 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 (AipsError x) { /// cout << x.getMesg() << 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-2.4.1/tables/DataMan/test/tMappedArrayEngine.out000066400000000000000000000001441321422335000232760ustar00rootroot00000000000000get 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-2.4.1/tables/DataMan/test/tMemoryStMan.cc000066400000000000000000000426171321422335000217470ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { cout << "Caught an exception: " << x.getMesg() << 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-2.4.1/tables/DataMan/test/tMemoryStMan.out000066400000000000000000001354711321422335000221720ustar00rootroot00000000000000after 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-2.4.1/tables/DataMan/test/tSSMAddRemove.cc000066400000000000000000000044761321422335000217660ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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, 28u); 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-2.4.1/tables/DataMan/test/tSSMAddRemove.in000066400000000000000000000046441321422335000220040ustar00rootroot00000000000000‹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-2.4.1/tables/DataMan/test/tSSMAddRemove.run000077500000000000000000000001411321422335000221710ustar00rootroot00000000000000#!/bin/sh rm -rf tSSMAddRemove_tmp.tab tar zxf tSSMAddRemove.in $casa_checktool ./tSSMAddRemove casacore-2.4.1/tables/DataMan/test/tSSMStringHandler.cc000066400000000000000000000277201321422335000226610ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { cout << "Caught an exception: " << x.getMesg() << 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-2.4.1/tables/DataMan/test/tSSMStringHandler.out000066400000000000000000007252511321422335000231070ustar00rootroot00000000000000after 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-2.4.1/tables/DataMan/test/tScaledArrayEngine.cc000066400000000000000000000170121321422335000230430ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (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. 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 (AipsError x) { /// cout << x.getMesg() << 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-2.4.1/tables/DataMan/test/tScaledArrayEngine.out000066400000000000000000000001441321422335000232630ustar00rootroot00000000000000get 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-2.4.1/tables/DataMan/test/tScaledComplexData.cc000066400000000000000000000245261321422335000230500ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { cout << "Caught an exception: " << x.getMesg() << endl; return 1; } return 0; // exit with success status } // First build a description. void a() { { // First ensure it can do the things it should. ScaledComplexData engine("", "", Complex(1.,1.), Complex(0.,0.)); Bool reask; AlwaysAssertExit (engine.canAccessArrayColumn(reask)); AlwaysAssertExit (engine.canAccessArrayColumnCells(reask)); AlwaysAssertExit (engine.canAccessSlice(reask)); AlwaysAssertExit (engine.canAccessColumnSlice(reask)); } // 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 (AipsError x) { cout << x.getMesg() << 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-2.4.1/tables/DataMan/test/tStArrayFile.cc000066400000000000000000000236751321422335000217240ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include #include #include #include #include #include #include #include // for sprintf #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 (AipsError x) { cout << "Caught an exception: " << x.getMesg() << 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); sprintf (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++) { sprintf (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++) { sprintf (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-2.4.1/tables/DataMan/test/tStArrayFile.out000066400000000000000000000045121321422335000221330ustar00rootroot00000000000000test 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-2.4.1/tables/DataMan/test/tStMan.cc000066400000000000000000000643461321422335000205610ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { cout << "Caught an exception: " << x.getMesg() << endl; return 1; } return 0; // exit with success status } casacore-2.4.1/tables/DataMan/test/tStMan1.cc000066400000000000000000000133251321422335000206310ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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. // // Create and fill a new table. void newtab (uInt nrrow, const DataManager& stman, uInt flushnr) { // Build the table description. TableDesc td("", "1", TableDesc::Scratch); td.addColumn (ScalarColumnDesc("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 "); } // 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 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 (AipsError x) { cout << "Caught an exception: " << x.getMesg() << endl; return 1; } return 0; // exit with success status } casacore-2.4.1/tables/DataMan/test/tStandardStMan.cc000066400000000000000000000371021321422335000222300ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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(); int main (int argc, const char* argv[]) { 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); } catch (AipsError x) { cout << "Caught an exception: " << x.getMesg() << 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-2.4.1/tables/DataMan/test/tStandardStMan.out000066400000000000000000002105541321422335000224560ustar00rootroot00000000000000after 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 [] casacore-2.4.1/tables/DataMan/test/tTSMShape.cc000066400000000000000000000074331321422335000211550ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 #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 (AipsError x) { cout << "Caught an exception: " << x.getMesg() << 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-2.4.1/tables/DataMan/test/tTiledCellStMan.cc000066400000000000000000000304321321422335000223300ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { cout << "Caught an exception: " << x.getMesg() << 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 // // 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&); int main () { try { writeFixed(TSMOption::Cache); readTable(TSMOption::MMap, True); writeNoHyper(TSMOption::MMap); readTable(TSMOption::Buffer, False); writeFixed(TSMOption::Buffer); readTable(TSMOption::Cache, False); } catch (AipsError x) { cout << "Caught an exception: " << x.getMesg() << 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>> 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-2.4.1/tables/DataMan/test/tTiledDataStM_1.cc000066400000000000000000000202001321422335000222130ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError& x) { cout << "Caught an exception: " << x.getMesg() << 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, 1u); 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, 0u); 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-2.4.1/tables/DataMan/test/tTiledDataStMan.cc000066400000000000000000000261461321422335000223310ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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(); int main() { try { a (); b (); } catch (AipsError x) { cout << "Caught an exception: " << x.getMesg() << 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-2.4.1/tables/DataMan/test/tTiledDataStMan.out000066400000000000000000000006221321422335000225420ustar00rootroot00000000000000>>> 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% <<< casacore-2.4.1/tables/DataMan/test/tTiledEmpty.cc000066400000000000000000000072031321422335000216040ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { cout << "Caught an exception: " << x.getMesg() << 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-2.4.1/tables/DataMan/test/tTiledFileAccess.cc000066400000000000000000000240311321422335000225050ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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); RegularFileIO fios(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 (AipsError x) { cout << "Exception: " << x.getMesg() << 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); RegularFileIO fios(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 (AipsError x) { cout << "Exception: " << x.getMesg() << 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); RegularFileIO fios(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 (AipsError x) { cout << "Exception: " << x.getMesg() << 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); RegularFileIO fios(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 (AipsError x) { cout << "Exception: " << x.getMesg() << 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); RegularFileIO fios(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 (AipsError x) { cout << "Exception: " << x.getMesg() << 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 (AipsError x) { cout << "Exception: " << x.getMesg() << endl; return 1; } } return 0; } casacore-2.4.1/tables/DataMan/test/tTiledFileAccess.out000066400000000000000000000006331321422335000227310ustar00rootroot000000000000000 >>> 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-2.4.1/tables/DataMan/test/tTiledShapeStM_1.cc000066400000000000000000000325621321422335000224200ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { cout << "Caught an exception: " << x.getMesg() << endl; return 1; } cout << "<<<" << endl; if (!ok) { return 1; } return 0; // exit with success status } casacore-2.4.1/tables/DataMan/test/tTiledShapeStMan.cc000066400000000000000000000530251321422335000225140ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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,1)); 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 (AipsError x) { cout << "Caught an exception: " << x.getMesg() << endl; return 1; } return 0; // exit with success status } casacore-2.4.1/tables/DataMan/test/tTiledShapeStMan.out000066400000000000000000000065071321422335000227410ustar00rootroot00000000000000WriteFixed ... 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-2.4.1/tables/DataMan/test/tTiledStMan.cc000066400000000000000000000066771321422335000215460ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { cout << "Caught an exception: " << x.getMesg() << 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 = 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(); } casacore-2.4.1/tables/DataMan/test/tVSCEngine.cc000066400000000000000000000106071321422335000213070ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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 // 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 (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. 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 ("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"); 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)); } //# 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 (AipsError x) { /// cout << x.getMesg() << endl; /// } } void b() { // Read back the table. Table tab("tVSCEngine_tmp.data"); ScalarColumn colx (tab, "x"); ScalarColumn coly (tab, "y"); ScalarColumn colA(tab, "colA"); Int valx; float valy; VSCExample valA; Int i; for (i=0; i<10; i++) { cout << "get row " << i << endl; colx.get (i, valx); coly.get (i, valy); colA.get (i, valA); if (valx != i || valy != i+1 || !(valA == VSCExample(i,i+1))) { cout << "error: " << valx << " " << valy << " " << valA.x() << " " << valA.y() << endl; } } Vector vecA = colA.getColumn(); for (i=0; i<10; i++) { if (!(vecA(i) == VSCExample(i,i+1))) { cout << "error in vecA(" << i << "): " << vecA(i).x() << " " << vecA(i).y() << endl; } } } casacore-2.4.1/tables/DataMan/test/tVSCEngine.out000066400000000000000000000001441321422335000215240ustar00rootroot00000000000000get 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-2.4.1/tables/DataMan/test/tVirtColEng.cc000066400000000000000000000141071321422335000215410ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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 (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. 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-2.4.1/tables/DataMan/test/tVirtColEng.out000066400000000000000000000002601321422335000217560ustar00rootroot00000000000000get 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-2.4.1/tables/DataMan/test/tVirtualTaQLColumn.cc000066400000000000000000000254601321422335000230570ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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(); 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(); } catch (AipsError x) { cout << "Caught an exception: " << x.getMesg() << 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("acalc")); td.addColumn (ScalarColumnDesc("acalc2")); td.addColumn (ScalarColumnDesc("acalc3")); td.addColumn (ArrayColumnDesc("arr1",3,ColumnDesc::Direct)); td.addColumn (ArrayColumnDesc("arr2",0)); td.addColumn (ArrayColumnDesc("arr3",0,ColumnDesc::Direct)); td.addColumn (ArrayColumnDesc("arrcalc",0)); 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 vtc("ab+10."); VirtualTaQLColumn vtc2("ag+max(arr3)"); VirtualTaQLColumn vtc3("ab*ac"); VirtualTaQLColumn vtac("ab*arr3"); newtab.bindColumn ("acalc", vtc); newtab.bindColumn ("acalc2", vtc2); newtab.bindColumn ("acalc3", vtc3); newtab.bindColumn ("arrcalc", vtac); 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); sprintf (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 acalc(tab,"acalc"); ScalarColumn acalc2(tab,"acalc2"); ScalarColumn acalc3(tab,"acalc3"); ScalarColumn acalc4(tab,"acalc4"); ArrayColumn arr1(tab,"arr1"); ArrayColumn arr2(tab,"arr2"); ArrayColumn arr3(tab,"arr3"); ArrayColumn arrcalc(tab,"arrcalc"); Int i; Short acalc3val; Int abval, acval; uInt adval; float aeval, acalcval, acalc4val; String afval; 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))); 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); acalc.get (i, acalcval); acalc2.get (i, acalc2val); acalc3.get (i, acalc3val); acalc4.get (i, acalc4val); sprintf (str, "V%i", i); if (abval != i || acval != i+1 || Int(adval) != i+2 || aeval != i+3 || afval != str || agval != DComplex(i+2) || acalcval != abval+10 || acalc3val != abval*acval) { cout << "error in row " << i << ": " << abval << ", " << acval << ", " << adval << ", " << aeval << ", " << afval << ", " << agval << ", " << acalcval << ", " << acalc3val << 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"); check (subset, False); } casacore-2.4.1/tables/LogTables.h000066400000000000000000000033431321422335000165730ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #if !defined (AIPS_MODULE_LOGTABLES_H) #define AIPS_MODULE_LOGTABLES_H #include // // // // Logging in Casacore tables // // //
      • Logging //
      • Tables // // // // // // // // // // // // // // // // #endif casacore-2.4.1/tables/LogTables/000077500000000000000000000000001321422335000164175ustar00rootroot00000000000000casacore-2.4.1/tables/LogTables/LogFilterExpr.cc000066400000000000000000000071721321422335000214630ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/tables/LogTables/LogFilterExpr.h000066400000000000000000000051461321422335000213240ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/tables/LogTables/LogFilterTaql.cc000066400000000000000000000042431321422335000214420ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/tables/LogTables/LogFilterTaql.h000066400000000000000000000072021321422335000213020ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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-2.4.1/tables/LogTables/LoggerHolder.cc000066400000000000000000000200031321422335000212760ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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-2.4.1/tables/LogTables/LoggerHolder.h000066400000000000000000000370241321422335000211530ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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 sematics). 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: CountedPtr 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(); // 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: // Copy constructor is not needed, thus forbidden. LogHolderIter (const LogHolderIter&); // Assignment is not needed, thus forbidden. LogHolderIter& operator= (const LogHolderIter&); 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-2.4.1/tables/LogTables/NewFile.cc000066400000000000000000000101761321422335000202640ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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 (Table::canDeleteTable(extra_error, value)) { try { Table::deleteTable(value); removed = True; } catch (AipsError xxx) { removed = False; extra_error = String("Error deleting table ") + value + ":" + xxx.getMesg(); } } } } else if (thefile.isSymLink()) { SymLink sfile = thefile; sfile.remove(); removed = True; } } catch (AipsError x) { extra_error = x.getMesg(); 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-2.4.1/tables/LogTables/NewFile.h000066400000000000000000000056201321422335000201240ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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-2.4.1/tables/LogTables/TableLogSink.cc000066400000000000000000000222631321422335000212510ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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() { 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, CountedPtr(sink)); } } //# NAMESPACE CASACORE - END casacore-2.4.1/tables/LogTables/TableLogSink.h000066400000000000000000000220601321422335000211060ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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: // Undefined and inaccessible TableLogSink(); // 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-2.4.1/tables/LogTables/test/000077500000000000000000000000001321422335000173765ustar00rootroot00000000000000casacore-2.4.1/tables/LogTables/test/CMakeLists.txt000066400000000000000000000004451321422335000221410ustar00rootroot00000000000000set (tests dLogging dLogging2 tLoggerHolder tLogging ) foreach (test ${tests}) add_executable (${test} ${test}.cc) target_link_libraries (${test} casa_tables) add_test (${test} ${CMAKE_SOURCE_DIR}/cmake/cmake_assay ./${test}) add_dependencies(check ${test}) endforeach (test) casacore-2.4.1/tables/LogTables/test/dLogging.cc000066400000000000000000000140041321422335000214360ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //#!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! //#!! //#!! 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 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-2.4.1/tables/LogTables/test/dLogging2.cc000066400000000000000000000157021321422335000215260ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //#!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! //#!! //#!! 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-2.4.1/tables/LogTables/test/tLoggerHolder.cc000066400000000000000000000072551321422335000224570ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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")) { Table::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 (AipsError& x) { cout << "Unexpected exception: " << x.getMesg() << endl; return 1; } catch (...) { cout << "Unexpected unknown exception" << endl; return 1; } cout << "OK" << endl; return 0; } casacore-2.4.1/tables/LogTables/test/tLogging.cc000066400000000000000000000452661321422335000214740ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { caught = True; AlwaysAssertExit(x.getMesg().contains("test")); AlwaysAssertExit(logTable.nrow() == 5 && logTable2.nrow() == 7); } AlwaysAssertExit(caught); // static void postGloballyThenThrow(const LogMessage &message); caught = False; try { sink5.postGloballyThenThrow(message); } catch (AipsError x) { caught = True; AlwaysAssertExit(x.getMesg().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 (AipsError 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 (AipsError x) { cout << "Caught an exception : " << x.getMesg() << endl; exit(1); } cerr << "OK (nothing else should have been printed by this program)\n"; return 0; } casacore-2.4.1/tables/TaQL.h000066400000000000000000000046331321422335000155230ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: Tables.h 21434 2014-05-07 13:07:20Z gervandiepen $ #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. // // } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/tables/TaQL/000077500000000000000000000000001321422335000153445ustar00rootroot00000000000000casacore-2.4.1/tables/TaQL/ExprAggrNode.cc000066400000000000000000000371451321422335000202120ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: TaQLNode.h 21051 2011-04-20 11:46:29Z gervandiepen $ //# Includes #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN TableExprAggrNode::TableExprAggrNode (FunctionType ftype, NodeDataType dtype, ValueType vtype, const TableExprNodeSet& source) : TableExprFuncNode (ftype, dtype, vtype, source) { // 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; } TableExprFuncNode::NodeDataType TableExprAggrNode::checkOperands (Block& dtypeOper, ValueType& resVT, FunctionType ftype, PtrBlock& nodes) { if (ftype >= FirstAggrArrayFunc && ftype < LastAggrArrayFunc && nodes.size() > 0 && nodes[0]->valueType() != VTArray) { throw TableInvExpr ("Argument of GxxxS functions has to be an array"); } 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; case gminFUNC: case gmaxFUNC: checkNumOfArg (1, 1, nodes); return checkDT (dtypeOper, NTReal, NTReal, nodes); case gsumsFUNC: case gproductsFUNC: case gsumsqrsFUNC: resVT = VTArray; case gsumFUNC: case gproductFUNC: case gsumsqrFUNC: checkNumOfArg (1, 1, nodes); return checkDT (dtypeOper, NTNumeric, NTNumeric, nodes); case gmeansFUNC: resVT = VTArray; case gmeanFUNC: checkNumOfArg (1, 1, nodes); return checkDT (dtypeOper, NTNumeric, NTDouCom, nodes); case gvariancesFUNC: case gstddevsFUNC: case grmssFUNC: resVT = VTArray; case gvarianceFUNC: case gstddevFUNC: 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; case ganyFUNC: case gallFUNC: checkNumOfArg (1, 1, nodes); return checkDT (dtypeOper, NTBool, NTBool, nodes); case gntruesFUNC: case gnfalsesFUNC: resVT = VTArray; case gntrueFUNC: case gnfalseFUNC: checkNumOfArg (1, 1, nodes); return checkDT (dtypeOper, NTBool, NTInt, nodes); default: throw TableInvExpr ("Unhandled aggregate function " + String::toString(ftype)); } } void TableExprAggrNode::getAggrNodes (vector& aggr) { aggr.push_back (this); uInt naggr = aggr.size(); for (uInt i=0; igetAggrNodes (aggr); } if (naggr != aggr.size()) { throw TableInvExpr ("The argument of an aggregate function cannot use " "an aggregate function"); } } CountedPtr TableExprAggrNode::makeGroupAggrFunc() { // Create a new function object because each FuncSet needs its own one. itsFunc = 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 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 gvarianceFUNC: return new TableExprGroupVarianceDouble(this); case gstddevFUNC: return new TableExprGroupStdDevDouble(this); 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); 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 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 gvarianceFUNC: return new TableExprGroupVarianceArrayDouble(this); case gstddevFUNC: return new TableExprGroupStdDevArrayDouble(this); 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); 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-2.4.1/tables/TaQL/ExprAggrNode.h000066400000000000000000000120261321422335000200430ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: TaQLNode.h 21051 2011-04-20 11:46:29Z gervandiepen $ #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); // Check the operands of the aggregate function and return the // result's data type. static NodeDataType checkOperands (Block& dtypeOper, ValueType& resVT, FunctionType ftype, PtrBlock& nodes); // Get the nodes representing an aggregate function. virtual void getAggrNodes (vector& aggr); // Get the operand node. TableExprNodeRep* operand() { return (operands().empty() ? 0 : operands()[0]); } // Create the correct aggregate function object. // It is also kept in case it is a lazy aggregate function. virtual CountedPtr 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. CountedPtr itsFunc; }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/tables/TaQL/ExprAggrNodeArray.cc000066400000000000000000000232431321422335000212030ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: TaQLNodeArray.h 21051 2011-04-20 11:46:29Z gervandiepen $ //# Includes #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN TableExprAggrNodeArray::TableExprAggrNodeArray (TableExprFuncNode::FunctionType ftype, NodeDataType dtype, ValueType vtype, const TableExprNodeSet& source, const TaQLStyle& style) : TableExprFuncNodeArray (ftype, dtype, vtype, source, 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; } void TableExprAggrNodeArray::getAggrNodes (vector& aggr) { aggr.push_back (this); uInt naggr = aggr.size(); for (uInt i=0; igetAggrNodes (aggr); } if (naggr != aggr.size()) { throw TableInvExpr ("The argument of an aggregate function cannot use " "an aggregate function"); } } CountedPtr TableExprAggrNodeArray::makeGroupAggrFunc() { // Create a new function object because each FuncSet needs its own one. itsFunc = doMakeGroupAggrFunc(); return itsFunc; } Bool TableExprAggrNodeArray::isLazyAggregate() const { return itsFunc->isLazy(); } CountedPtr TableExprAggrNodeArray::doMakeGroupAggrFunc() { if (funcType() == TableExprFuncNode::gexpridFUNC) { return new TableExprGroupExprId(this); } else if (funcType() == TableExprFuncNode::gaggrFUNC) { return new TableExprGroupAggr(this); } else if (funcType() == TableExprFuncNode::growidFUNC) { return new TableExprGroupRowid(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 new TableExprGroupHistScalar (this, nbin, start, end); } if (operands()[0]->dataType() == NTInt) { return new TableExprGroupHistInt (this, nbin, start, end); } return new TableExprGroupHistDouble (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 new TableExprGroupArrayAnys(this); case TableExprFuncNode::gallsFUNC: return new TableExprGroupArrayAlls(this); case TableExprFuncNode::gntruesFUNC: return new TableExprGroupArrayNTrues(this); case TableExprFuncNode::gnfalsesFUNC: return new TableExprGroupArrayNFalses(this); default: throw TableInvExpr ("Aggregate function " + String::toString(funcType()) + " cannot be used with a bool argument"); } case NTInt: switch (funcType()) { case TableExprFuncNode::gminsFUNC: return new TableExprGroupMinsArrayInt(this); case TableExprFuncNode::gmaxsFUNC: return new TableExprGroupMaxsArrayInt(this); case TableExprFuncNode::gsumsFUNC: return new TableExprGroupSumsArrayInt(this); case TableExprFuncNode::gproductsFUNC: return new TableExprGroupProductsArrayInt(this); case TableExprFuncNode::gsumsqrsFUNC: return new TableExprGroupSumSqrsArrayInt(this); default: break; } // Fall through, so e.g. mean of ints can be done case NTDouble: switch (funcType()) { case TableExprFuncNode::gminsFUNC: return new TableExprGroupMinsArrayDouble(this); case TableExprFuncNode::gmaxsFUNC: return new TableExprGroupMaxsArrayDouble(this); case TableExprFuncNode::gsumsFUNC: return new TableExprGroupSumsArrayDouble(this); case TableExprFuncNode::gproductsFUNC: return new TableExprGroupProductsArrayDouble(this); case TableExprFuncNode::gsumsqrsFUNC: return new TableExprGroupSumSqrsArrayDouble(this); case TableExprFuncNode::gmeansFUNC: return new TableExprGroupMeansArrayDouble(this); case TableExprFuncNode::gvariancesFUNC: return new TableExprGroupVariancesArrayDouble(this); case TableExprFuncNode::gstddevsFUNC: return new TableExprGroupStdDevsArrayDouble(this); case TableExprFuncNode::grmssFUNC: return new TableExprGroupRmssArrayDouble(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 new TableExprGroupSumsArrayDComplex(this); case TableExprFuncNode::gproductsFUNC: return new TableExprGroupProductsArrayDComplex(this); case TableExprFuncNode::gsumsqrsFUNC: return new TableExprGroupSumSqrsArrayDComplex(this); case TableExprFuncNode::gmeansFUNC: return new TableExprGroupMeansArrayDComplex(this); 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-2.4.1/tables/TaQL/ExprAggrNodeArray.h000066400000000000000000000073711321422335000210510ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: TaQLNode.h 21051 2011-04-20 11:46:29Z gervandiepen $ #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 TaQLStyle& style); // Get the nodes representing an aggregate function. virtual void getAggrNodes (vector& aggr); // Get the operand node. TableExprNodeRep* operand() { return (operands().empty() ? 0 : operands()[0]); } // Create the correct aggregate function object. virtual CountedPtr 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. CountedPtr doMakeGroupAggrFunc(); //# Data members. CountedPtr itsFunc; }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/tables/TaQL/ExprConeNode.cc000066400000000000000000000531101321422335000202040ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: ExprConeNode.cc 21262 2012-09-07 12:38:36Z gervandiepen $ #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN TableExprConeNode::TableExprConeNode (FunctionType ftype, NodeDataType dtype, const TableExprNodeSet& source, uInt origin) : TableExprFuncNode (ftype, dtype, VTScalar, source), origin_p (origin) {} TableExprConeNode::~TableExprConeNode() {} // Fill the children pointers of a node. // Also reduce the tree if possible by combining constants. // When one of the nodes is a constant, convert its type if // it does not match the other one. TableExprNodeRep* TableExprConeNode::fillNode (TableExprConeNode* thisNode, PtrBlock& nodes, const Block& dtypeOper) { return TableExprFuncNode::fillNode (thisNode, nodes, dtypeOper); } // Fill the children pointers of a node. void TableExprConeNode::fillChildNodes (TableExprConeNode* thisNode, PtrBlock& nodes, const Block& dtypeOper) { TableExprFuncNode::fillChildNodes (thisNode, nodes, dtypeOper); } void TableExprConeNode::tryToConst() { } 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 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 (uInt i=0; i srcArr = operands_p[0]->getArrayDouble(id).array(); if (srcArr.nelements() != 2) { throw TableInvExpr("First CONES argument must have 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 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 (uInt i=0; i srcArr = operands()[0]->getArrayDouble(id).array(); if (srcArr.nelements() != 2) { throw TableInvExpr("First CONES argument " "must have 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 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]; Int res = -1; for (uInt i=0; i srcArr = operands()[0]->getArrayDouble(id).array(); if (srcArr.nelements() != 2) { throw TableInvExpr("First FINDCONE argument " "must have 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]; Int res = -1; for (uInt 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::getDouble, " "unknown function")); } return 0; } TableExprNodeRep::NodeDataType TableExprConeNode::checkOperands (Block& dtypeOper, ValueType& resVT, Block&, FunctionType fType, PtrBlock& 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 TableExprNodeRep* node) { Int nelem = -1; if (node->valueType() == VTSet) { const TableExprNodeSet* set = dynamic_cast(node); AlwaysAssert (set, AipsError); TableExprNodeRep* arr = set->setOrArray(); if (arr->valueType() != VTArray) { throw TableInvExpr ("CONES argument is a non-array set"); } nelem = arr->shape().product(); delete arr; } else { nelem = node->shape().product(); } return nelem; } TableExprConeNodeArray::TableExprConeNodeArray (TableExprFuncNode::FunctionType ftype, NodeDataType dtype, const TableExprNodeSet& source, uInt origin) : TableExprFuncNodeArray (ftype, dtype, VTArray, source, TaQLStyle()), origin_p (origin) { ndim_p = -1; } TableExprConeNodeArray::~TableExprConeNodeArray() {} // Fill the children pointers of a node. // Also reduce the tree if possible by combining constants. // When one of the nodes is a constant, convert its type if // it does not match the other one. TableExprNodeRep* TableExprConeNodeArray::fillNode (TableExprConeNodeArray* thisNode, PtrBlock& nodes, const Block& dtypeOper) { return TableExprFuncNodeArray::fillNode (thisNode, nodes, dtypeOper); } // Fill the children pointers of a node. void TableExprConeNodeArray::fillChildNodes (TableExprConeNodeArray* thisNode, PtrBlock& nodes, const Block& dtypeOper) { TableExprFuncNode::fillChildNodes (thisNode->getChild(), nodes, dtypeOper); } void TableExprConeNodeArray::tryToConst() { } 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 (uInt j=0; j(resArr); } case TableExprFuncNode::cones3FUNC: { Array srcArr = operands()[0]->getArrayDouble(id).array(); if (srcArr.nelements() != 2) { 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 (uInt 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 (uInt 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; 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 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); if (rad != &radval) { rad = radArr.getStorage (deleteRad); } Array resArr(shp); Int64* res = resArr.data(); for (uInt j=0; j= 0) break; } res++; } srcArr.freeStorage (src, deleteSrc); coneArr.freeStorage (cone, deleteCone); if (rad != &radval) { radArr.freeStorage (rad, deleteRad); } return MArray(resArr); } default: throw (TableInvExpr ("TableExprConeNodeArray::getArrayDouble, " "unknown function")); } } } //# NAMESPACE CASACORE - END casacore-2.4.1/tables/TaQL/ExprConeNode.h000066400000000000000000000124001321422335000200430ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: ExprConeNode.h 21262 2012-09-07 12:38:36Z gervandiepen $ #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, 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, PtrBlock&); // Link the children to the node and convert the children // to constants if possible. Also convert the node to // constant if possible. static TableExprNodeRep* fillNode (TableExprConeNode* thisNode, PtrBlock& nodes, const Block& dtypeOper); // Link the children to the node and convert the children // to constants if possible. static void fillChildNodes (TableExprConeNode* thisNode, PtrBlock& nodes, const Block& dtypeOper); private: // Try if the function gives a constant result. // If so, set the expression type to Constant. void tryToConst(); // Find the number of elements in an argument. // It returns -1 if unknown. static Int findNelem (const TableExprNodeRep* node); uInt origin_p; }; class TableExprConeNodeArray : public TableExprFuncNodeArray { public: // Constructor TableExprConeNodeArray (TableExprFuncNode::FunctionType, NodeDataType, const TableExprNodeSet& source, uInt origin); // Destructor ~TableExprConeNodeArray(); // 'get' Functions to get the desired result of a function. // MArray getArrayBool (const TableExprId& id); MArray getArrayInt (const TableExprId& id); // // Link the children to the node and convert the children // to constants if possible. Also convert the node to // constant if possible. static TableExprNodeRep* fillNode (TableExprConeNodeArray* thisNode, PtrBlock& nodes, const Block& dtypeOper); // Link the children to the node and convert the children // to constants if possible. static void fillChildNodes (TableExprConeNodeArray* thisNode, PtrBlock& nodes, const Block& dtypeOper); private: // Try if the function gives a constant result. // If so, set the expression type to Constant. void tryToConst(); uInt origin_p; }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/tables/TaQL/ExprDerNode.cc000066400000000000000000000262201321422335000200340ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: ExprDerNode.cc 21521 2014-12-10 08:06:42Z gervandiepen $ #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, Table()), value_p (val) {} TableExprNodeConstBool::~TableExprNodeConstBool() {} Bool TableExprNodeConstBool::getBool (const TableExprId&) { return value_p; } TableExprNodeConstInt::TableExprNodeConstInt (const Int64& val) : TableExprNodeBinary (NTInt, VTScalar, OtLiteral, Table()), value_p (val) {} TableExprNodeConstInt::~TableExprNodeConstInt() {} 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, Table()), value_p (val) {} TableExprNodeConstDouble::~TableExprNodeConstDouble() {} 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, Table()), value_p (val) {} TableExprNodeConstDComplex::~TableExprNodeConstDComplex() {} DComplex TableExprNodeConstDComplex::getDComplex (const TableExprId&) { return value_p; } TableExprNodeConstString::TableExprNodeConstString (const String& val) : TableExprNodeBinary (NTString, VTScalar, OtLiteral, Table()), value_p (val) {} TableExprNodeConstString::~TableExprNodeConstString() {} String TableExprNodeConstString::getString (const TableExprId&) { return value_p; } TableExprNodeConstRegex::TableExprNodeConstRegex (const TaqlRegex& val) : TableExprNodeBinary (NTRegex, VTScalar, OtLiteral, Table()), value_p (val) {} TableExprNodeConstRegex::~TableExprNodeConstRegex() {} TaqlRegex TableExprNodeConstRegex::getRegex (const TableExprId&) { return value_p; } TableExprNodeConstDate::TableExprNodeConstDate (const MVTime& val) : TableExprNodeBinary (NTDate, VTScalar, OtLiteral, Table()), value_p (val) {} TableExprNodeConstDate::~TableExprNodeConstDate() {} 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 Table& table, const String& name) : TableExprNodeBinary (NTNumeric, VTScalar, OtColumn, table), selTable_p (table), tabCol_p (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)); } 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; } TableExprNodeColumn::~TableExprNodeColumn() {} void TableExprNodeColumn::getColumnNodes (vector& cols) { cols.push_back (this); } 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(); selTable_p = selTable_p(rownrs); tabCol_p = TableColumn(selTable_p, 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::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 Table& table, uInt origin) : TableExprNodeBinary (NTInt, VTScalar, OtRownr, table), origin_p (origin) {} TableExprNodeRownr::~TableExprNodeRownr () {} Int64 TableExprNodeRownr::getInt (const TableExprId& id) { AlwaysAssert (id.byRow(), AipsError); return id.rownr() + origin_p; } TableExprNodeRowid::TableExprNodeRowid (const Table& table) : TableExprNodeBinary (NTInt, VTScalar, OtRownr, table), rownrs_p (table.nrow()) { indgen (rownrs_p); } TableExprNodeRowid::~TableExprNodeRowid () {} void TableExprNodeRowid::applySelection (const Vector& rownrs) { // Append rows for an insert. if (rownrs.size() == 1 && rownrs[0] >= rownrs_p.size()) { uInt sz = rownrs_p.size(); rownrs_p.resize (rownrs[0], True); for (uInt i=sz; i newRows(rownrs.size()); for (uInt 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(); Bool getBool (const TableExprId& id); 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(); Int64 getInt (const TableExprId& id); Double getDouble (const TableExprId& id); DComplex getDComplex (const TableExprId& id); 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(); Double getDouble (const TableExprId& id); DComplex getDComplex (const TableExprId& id); 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(); DComplex getDComplex (const TableExprId& id); 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(); String getString (const TableExprId& id); 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(); TaqlRegex getRegex (const TableExprId& id); 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(); Double getDouble(const TableExprId& id); MVTime getDate (const TableExprId& id); 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 Table&, const String& columnName); ~TableExprNodeColumn(); // This node represents a table column. virtual void getColumnNodes (vector& cols); // Do not apply the selection. virtual void disableApplySelection(); // Re-create the column object for a selection of rows. virtual void applySelection (const Vector& rownrs); // Get the data type of this scalar column. Bool getColumnDataType (DataType&) const; // Get the data for the given id. 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); const TableColumn& getColumn() const; // Get the data for the given rows. Array getColumnBool (const Vector& rownrs); Array getColumnuChar (const Vector& rownrs); Array getColumnShort (const Vector& rownrs); Array getColumnuShort (const Vector& rownrs); Array getColumnInt (const Vector& rownrs); Array getColumnuInt (const Vector& rownrs); Array getColumnFloat (const Vector& rownrs); Array getColumnDouble (const Vector& rownrs); Array getColumnComplex (const Vector& rownrs); Array getColumnDComplex (const Vector& rownrs); Array getColumnString (const Vector& rownrs); // Get the column unit (can be empty). static Unit getColumnUnit (const TableColumn&); protected: Table selTable_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 an other value (1 in TaQL) // class TableExprNodeRownr : public TableExprNodeBinary { public: TableExprNodeRownr (const Table&, uInt origin); ~TableExprNodeRownr(); Int64 getInt (const TableExprId& id); private: 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 Table&); ~TableExprNodeRowid(); virtual void applySelection (const Vector& rownrs); Int64 getInt (const TableExprId& id); private: 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 Table&); ~TableExprNodeRandom(); Double getDouble (const TableExprId& id); private: MLCG generator_p; Uniform random_p; }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/tables/TaQL/ExprDerNodeArray.cc000066400000000000000000000246241321422335000210410ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: ExprDerNodeArray.cc 21262 2012-09-07 12:38:36Z gervandiepen $ #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 (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 (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-2.4.1/tables/TaQL/ExprDerNodeArray.h000066400000000000000000000202301321422335000206700ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: ExprDerNodeArray.h 21262 2012-09-07 12:38:36Z gervandiepen $ #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 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-2.4.1/tables/TaQL/ExprFuncNode.cc000066400000000000000000001611741321422335000202250ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: ExprFuncNode.cc 21277 2012-10-31 16:07:31Z gervandiepen $ #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 Table& table) : TableExprNodeMulti (dtype, vtype, OtFunc, source), funcType_p (ftype), argDataType_p (dtype), scale_p (1), table_p (table) {} TableExprFuncNode::~TableExprFuncNode() {} // Fill the children pointers of a node. // Also reduce the tree if possible by combining constants. // If one of the nodes is a constant, convert its type if // it does not match the other one. TableExprNodeRep* TableExprFuncNode::fillNode (TableExprFuncNode* thisNode, PtrBlock& nodes, const Block& dtypeOper) { // Fill child nodes as needed. It also fills operands_p. fillChildNodes (thisNode, nodes, dtypeOper); // Set the unit for some functions. Double scale = fillUnits (thisNode, thisNode->operands_p, thisNode->funcType()); thisNode->setScale (scale); // Some functions on a variable can already give a constant result. thisNode->tryToConst(); if (thisNode->operands_p.nelements() > 0) { return convertNode (thisNode, True); } return thisNode; } Double TableExprFuncNode::fillUnits (TableExprNodeRep* node, PtrBlock& nodes, FunctionType func) { Double scale = 1; if (func == cFUNC) { node->setUnit ("m/s"); } if (nodes.nelements() > 0) { const Unit& childUnit = nodes[0]->unit(); switch (func) { case asinFUNC: case acosFUNC: case atanFUNC: case atan2FUNC: case timeFUNC: case argFUNC: // These functions return radians. node->setUnit ("rad"); break; case mjdFUNC: // These functions return days. node->setUnit ("d"); break; case mjdtodateFUNC: // These functions require days. if (! childUnit.empty()) { TableExprNodeUnit::adaptUnit (nodes[0], "d"); } break; case absFUNC: case realFUNC: case imagFUNC: case conjFUNC: case roundFUNC: case floorFUNC: case ceilFUNC: case fmodFUNC: case arrsumFUNC: case arrsumsFUNC: 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 arrstddevFUNC: case arrstddevsFUNC: case runstddevFUNC: case boxstddevFUNC: 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 arrayFUNC: case transposeFUNC: 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 gstddevFUNC: case grmsFUNC: case gminsFUNC: case gmaxsFUNC: case gsumsFUNC: case gmeansFUNC: case gstddevsFUNC: case grmssFUNC: case gaggrFUNC: case gmedianFUNC: case gfractileFUNC: // These functions return the same unit as their child. node->setUnit (childUnit); break; case normFUNC: case squareFUNC: case arrsumsqrFUNC: case arrsumsqrsFUNC: case gsumsqrFUNC: case gsumsqrsFUNC: case arrvarianceFUNC: case arrvariancesFUNC: case runvarianceFUNC: case boxvarianceFUNC: case gvarianceFUNC: case gvariancesFUNC: // These functions return the square of their child. if (! childUnit.empty()) { Quantity q(1., childUnit); node->setUnit ((q*q).getFullUnit()); } break; case cubeFUNC: if (! childUnit.empty()) { Quantity q(1., childUnit); node->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 = qs.getValue(); node->setUnit (qs.getFullUnit()); } break; case sinFUNC: case cosFUNC: case tanFUNC: case hmsFUNC: case dmsFUNC: case hdmsFUNC: // These functions return no unit, but their child must be in radians. if (! childUnit.empty()) { TableExprNodeUnit::adaptUnit (nodes[0], "rad"); } break; case iifFUNC: node->setUnit (makeEqualUnits (nodes, 1, nodes.nelements())); break; case complexFUNC: case minFUNC: case maxFUNC: node->setUnit (makeEqualUnits (nodes, 0, nodes.nelements())); break; case near2FUNC: case nearabs2FUNC: case near3FUNC: makeEqualUnits (nodes, 0, 2); break; case nearabs3FUNC: makeEqualUnits (nodes, 0, 3); break; case angdistFUNC: case angdistxFUNC: node->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) { uInt i; // Copy block of children. // Determine if common argument type is Int, Double or Complex. // (this is used by some functions like near and norm). thisNode->operands_p.resize (nodes.nelements()); thisNode->argDataType_p = NTInt; for (i=0; ioperands_p[i] = nodes[i]->link(); if (nodes[i]->dataType() == NTDouble && thisNode->argDataType_p != NTComplex) { thisNode->argDataType_p = NTDouble; } else if (nodes[i]->dataType() == NTComplex) { thisNode->argDataType_p = NTComplex; } } // Convert String to Date if needed for (i=0; idataType() == NTString) { TableExprNode dNode = datetime (thisNode->operands_p[i]); unlink (thisNode->operands_p[i]); thisNode->operands_p[i] = getRep (dNode)->link(); } else if (nodes[i]->dataType() == NTDouble) { TableExprNode dNode = mjdtodate (thisNode->operands_p[i]); unlink (thisNode->operands_p[i]); thisNode->operands_p[i] = getRep (dNode)->link(); } } } } 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().nelements() > 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 anyFUNC: if (operands_p[0]->valueType() == VTArray) { return anyTrue (operands_p[0]->getArrayBool(id)); } return operands_p[0]->getBool (id); case allFUNC: 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; TableParseSelect::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 { const TableRecord& colkeys (TableColumn(table_p, columnName).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 ntrueFUNC: if (operands_p[0]->valueType() == VTArray) { return ntrue (operands_p[0]->getArrayBool (id)); } return (operands_p[0]->getBool(id) ? 1 : 0); case nfalseFUNC: 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).nelements()); } 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); } switch(funcType_p) { case piFUNC: return C::pi; case eFUNC: return C::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.) * C::_2pi; 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 arrvarianceFUNC: if (operands_p[0]->valueType() == VTArray) { return variance (operands_p[0]->getArrayDouble (id)); } return 0; case arrstddevFUNC: if (operands_p[0]->valueType() == VTArray) { return stddev (operands_p[0]->getArrayDouble (id)); } return 0; case arravdevFUNC: if (operands_p[0]->valueType() == VTArray) { 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 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 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 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; 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))); case patternFUNC: return TaqlRegex(Regex(Regex::fromPattern(operands_p[0]->getString (id)))); case sqlpatternFUNC: return TaqlRegex(Regex(Regex::fromSQLPattern(operands_p[0]->getString (id)))); 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 PtrBlock& 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 with optional spaces). Vector fmts = stringToVector(fmt, '|'); 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, PtrBlock& nodes) { uInt i; // 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 arrvarianceFUNC: case arrstddevFUNC: case arravdevFUNC: 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 anyFUNC: case allFUNC: checkNumOfArg (1, 1, nodes); return checkDT (dtypeOper, NTBool, NTBool, nodes); case ntrueFUNC: case nfalseFUNC: 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 or scalar 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 arrvariancesFUNC: case arrstddevsFUNC: case arravdevsFUNC: case arrrmssFUNC: case arrmediansFUNC: case arrfractilesFUNC: case anysFUNC: case allsFUNC: case ntruesFUNC: case nfalsesFUNC: case runminFUNC: case runmaxFUNC: case runmeanFUNC: case runvarianceFUNC: case runstddevFUNC: case runavdevFUNC: case runrmsFUNC: case runmedianFUNC: case runanyFUNC: case runallFUNC: case boxminFUNC: case boxmaxFUNC: case boxmeanFUNC: case boxvarianceFUNC: case boxstddevFUNC: case boxavdevFUNC: case boxrmsFUNC: case boxmedianFUNC: case boxanyFUNC: case boxallFUNC: case arrayFUNC: case transposeFUNC: case diagonalFUNC: case resizeFUNC: { // 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: dtin = dtout = NTNumeric; break; case arrmeansFUNC: case runmeanFUNC: case boxmeanFUNC: dtin = NTNumeric; dtout = NTDouCom; break; case arrfractilesFUNC: axarg = 2; break; case anysFUNC: case allsFUNC: case runanyFUNC: case runallFUNC: case boxanyFUNC: case boxallFUNC: dtin = dtout = NTBool; break; case ntruesFUNC: case nfalsesFUNC: dtin = NTBool; dtout = NTInt; break; case resizeFUNC: optarg = 1; case arrayFUNC: case transposeFUNC: 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. PtrBlock 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 an 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 ("The 3rd argument of 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 (i=0; i< nodes.nelements(); 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 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 scalar"); } 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] = new TableExprNodeConstString ("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] = new TableExprNodeConstDate (MVTime(Time())); return NTDate; case yearFUNC: case monthFUNC: case dayFUNC: case weekdayFUNC: case weekFUNC: dtout = NTInt; 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] = new TableExprNodeConstDate (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] = new TableExprNodeConstDate (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 int 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: checkNumOfArg (1, 1, nodes); return checkDT (dtypeOper, NTNumeric, NTDouCom, 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 asinFUNC: case acosFUNC: case atanFUNC: case tanFUNC: case tanhFUNC: checkNumOfArg (1, 1, nodes); return checkDT (dtypeOper, NTReal, 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. PtrBlock nodeTol(1); nodeTol[0] = nodes[2]; checkDT (dtypeOper, NTReal, NTBool, nodeTol); 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); PtrBlock nodeCond(1); nodeCond[0] = nodes[0]; PtrBlock nodeArg(2); nodeArg[0] = nodes[1]; nodeArg[1] = nodes[2]; Block dtypeTmp; checkDT (dtypeTmp, NTBool, NTBool, nodeCond); dtypeOper.resize(3); dtypeOper[0] = dtypeTmp[0]; NodeDataType dt = checkDT (dtypeTmp, NTAny, NTAny, nodeArg); dtypeOper[1] = dtypeTmp[0]; dtypeOper[2] = dtypeTmp[1]; return dt; } default: break; } // The following functions accept scalars only (or no arguments). for (i=0; i< nodes.nelements(); 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-2.4.1/tables/TaQL/ExprFuncNode.h000066400000000000000000000413611321422335000200620ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: ExprFuncNode.h 21277 2012-10-31 16:07:31Z gervandiepen $ #ifndef TABLES_EXPRFUNCNODE_H #define TABLES_EXPRFUNCNODE_H //# Includes #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 argFUNC, //# 23 // for Int, Double, DComplex, Bool or String returning Double realFUNC, //# 24 imagFUNC, //# 25 // for Int, Double, Bool or String returning Int (using floor) intFUNC, //# 26 // for Int or Double returning Double 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 Int, Double or DComplex returning DComplex complexFUNC, //# 38 // for Int, Double or Complex array returning the same arrsumFUNC, //# 39 arrsumsFUNC, //# 40 arrproductFUNC, //# 41 arrproductsFUNC, //# 42 arrsumsqrFUNC, //# 43 arrsumsqrsFUNC, //# 44 // for Int or Double array returning Int or Double arrminFUNC, //# 45 arrminsFUNC, //# 46 runminFUNC, //# 47 boxminFUNC, //# 48 arrmaxFUNC, //# 49 arrmaxsFUNC, //# 50 runmaxFUNC, //# 51 boxmaxFUNC, //# 52 // for Int or Double array returning Double arrmeanFUNC, //# 53 arrmeansFUNC, //# 54 runmeanFUNC, //# 55 boxmeanFUNC, //# 56 arrvarianceFUNC, //# 57 arrvariancesFUNC, //# 58 runvarianceFUNC, //# 59 boxvarianceFUNC, //# 60 arrstddevFUNC, //# 61 arrstddevsFUNC, //# 62 runstddevFUNC, //# 63 boxstddevFUNC, //# 64 arravdevFUNC, //# 65 arravdevsFUNC, //# 66 runavdevFUNC, //# 67 boxavdevFUNC, //# 68 arrrmsFUNC, //# 69 arrrmssFUNC, //# 70 runrmsFUNC, //# 71 boxrmsFUNC, //# 72 arrmedianFUNC, //# 73 arrmediansFUNC, //# 74 runmedianFUNC, //# 75 boxmedianFUNC, //# 76 arrfractileFUNC, //# 77 arrfractilesFUNC, //# 78 // for Bool array returning Bool anyFUNC, //# 79 anysFUNC, //# 80 runanyFUNC, //# 81 boxanyFUNC, //# 82 allFUNC, //# 83 allsFUNC, //# 84 runallFUNC, //# 85 boxallFUNC, //# 86 // for Bool array returning Int scalar ntrueFUNC, //# 87 ntruesFUNC, //# 88 nfalseFUNC, //# 89 nfalsesFUNC, //# 90 // for any type returning array of that type arrayFUNC, //# 91 transposeFUNC, //# 92 resizeFUNC, //# 93 diagonalFUNC, //# 94 // for Int, Double or DComplex array returning Bool isnanFUNC, //# 95 isinfFUNC, //# 96 isfiniteFUNC, //# 97 // for any array returning Bool scalar isdefFUNC, //# 98 isnullFUNC, //# 99 iscolFUNC, //# 100 iskeyFUNC, //# 101 // for any array returning Int scalar ndimFUNC, //# 102 nelemFUNC, //# 103 // for any array returning Int array shapeFUNC, //# 104 // for String strlengthFUNC, //# 105 returning Int upcaseFUNC, //# 106 returning String downcaseFUNC, //# 107 returning String capitalizeFUNC, //# 108 returning String trimFUNC, //# 109 returning String ltrimFUNC, //# 110 returning String rtrimFUNC, //# 111 returning String substrFUNC, //# 112 returning String replaceFUNC, //# 113 returning String regexFUNC, //# 114 returning TaqlRegex patternFUNC, //# 115 returning TaqlRegex sqlpatternFUNC, //# 116 returning TaqlRegex // for Date datetimeFUNC, //# 117 returning Date mjdtodateFUNC, //# 118 returning Date mjdFUNC, //# 119 returning Double dateFUNC, //# 120 returning Date timeFUNC, //# 121 returning Double (in radians) yearFUNC, //# 122 returning Int monthFUNC, //# 123 returning Int dayFUNC, //# 124 returning Int cmonthFUNC, //# 125 returning String weekdayFUNC, //# 126 returning Int cdowFUNC, //# 127 returning String weekFUNC, //# 128 returning Int ctodFUNC, //# 129 returning String cdateFUNC, //# 130 returning String ctimeFUNC, //# 131 returning String // return values as strings stringFUNC, //# 132 // return angles as hms strings hmsFUNC, //# 133 // return angles as dms strings dmsFUNC, //# 134 // return angles as hms/dms strings hdmsFUNC, //# 135 // special function returning a random Double number randFUNC, //# 136 // special function returning Int row number rownrFUNC, //# 137 // special function returning Int row id (meant for GIVING) rowidFUNC, //# 138 // special function resembling if statement iifFUNC, //# 139 // angular distance returning radians angdistFUNC, //# 140 angdistxFUNC, //# 141 // cone search functions, implemented in derived class conesFUNC, //# 142 cones3FUNC, //# 143 anyconeFUNC, //# 144 anycone3FUNC, //# 145 findconeFUNC, //# 146 findcone3FUNC, //# 147 // for Int, Double, Complex or String returning Bool boolFUNC, //# 148 // masked array functions nullarrayFUNC, //# 149 marrayFUNC, //# 150 arrdataFUNC, //# 151 arrmaskFUNC, //# 152 negatemaskFUNC, //# 153 replmaskedFUNC, //# 154 replunmaskedFUNC, //# 155 arrflatFUNC, //# 156 //# AGGREGATE functions must be the last ones. FirstAggrFunc, //# 157 countallFUNC = FirstAggrFunc, gcountFUNC, gfirstFUNC, glastFUNC, //# Grouping doing aggregation on the fly; reducing to a scalar per group gminFUNC, //# 161 gmaxFUNC, gsumFUNC, gproductFUNC, gsumsqrFUNC, gmeanFUNC, gvarianceFUNC, gstddevFUNC, grmsFUNC, ganyFUNC, gallFUNC, gntrueFUNC, gnfalseFUNC, //# Grouping doing aggregation on the fly; reducing to an array per group FirstAggrArrayFunc,//# 174 gminsFUNC = FirstAggrArrayFunc, gmaxsFUNC, gsumsFUNC, gproductsFUNC, gsumsqrsFUNC, gmeansFUNC, gvariancesFUNC, gstddevsFUNC, grmssFUNC, ganysFUNC, gallsFUNC, gntruesFUNC, gnfalsesFUNC, LastAggrArrayFunc,//# 187 ghistFUNC = LastAggrArrayFunc, //# Grouping requiring aggregation of rows when getting result gaggrFUNC, //# 188 growidFUNC, gmedianFUNC, gfractileFUNC, gexpridFUNC, //# special function (can be inserted by TableParse) NRFUNC //# 193 should be last }; // Constructor TableExprFuncNode (FunctionType, NodeDataType, ValueType, const TableExprNodeSet& source, const Table& = Table()); // 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, PtrBlock&); // 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). static Double fillUnits (TableExprNodeRep* node, PtrBlock& nodes, FunctionType func); // Link the children to the node and convert the children // to constants if possible. Also convert the node to // constant if possible. static TableExprNodeRep* fillNode (TableExprFuncNode* thisNode, PtrBlock& nodes, const Block& dtypeOper); // Link the children to the node and convert the children // to constants if possible. static void fillChildNodes (TableExprFuncNode* thisNode, PtrBlock& nodes, const Block& dtypeOper); // Set unit scale factor (needed for sqrt). void setScale (Double scale) { scale_p = scale; } // Get possible unit scale factor (needed for sqrt). Double getScale() const { return scale_p; } // Some functions to be used by TableExprNodeFuncArray. // const PtrBlock& operands() const { return operands_p; } PtrBlock& 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 PtrBlock& 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 (PtrBlock& 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-2.4.1/tables/TaQL/ExprFuncNodeArray.cc000066400000000000000000002500061321422335000212150ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: ExprFuncNodeArray.cc 21277 2012-10-31 16:07:31Z gervandiepen $ #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.nelements(); size_t n = res.nelements(); 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, TableExprNodeRep* 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 PtrBlock& 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.nelements(); // 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, TableExprNodeRep* operand2, const TableExprId& id, Bool maskValue) { if (!arr.hasMask()) { return arr; } 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; } 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) { const IPosition& shp = getArrayShape(id); 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 TaQLStyle& style) : TableExprNodeArray (dtype, OtFunc), node_p (ftype, dtype, vtype, source), origin_p (style.origin()), isCOrder_p (style.isCOrder()), constAxes_p (False), constAlt_p (False) { table_p = source.table(); exprtype_p = Variable; } TableExprFuncNodeArray::~TableExprFuncNodeArray() {} void TableExprFuncNodeArray::getAggrNodes (vector& aggr) { node_p.getAggrNodes (aggr); } void TableExprFuncNodeArray::getColumnNodes (vector& cols) { node_p.getColumnNodes (cols); } // Fill the children pointers of a node. // Also reduce the tree if possible by combining constants. // When one of the nodes is a constant, convert its type if // it does not match the other one. TableExprNodeRep* TableExprFuncNodeArray::fillNode (TableExprFuncNodeArray* thisNode, PtrBlock& nodes, const Block& dtypeOper) { // Fill child nodes as needed. TableExprFuncNode::fillChildNodes (thisNode->getChild(), nodes, dtypeOper); // Set the resulting unit. Double scale = TableExprFuncNode::fillUnits (thisNode, thisNode->rwOperands(), thisNode->funcType()); thisNode->setScale (scale); // Some functions on a variable can already give a constant result. thisNode->tryToConst(); if (thisNode->operands().nelements() > 0) { return convertNode (thisNode, True); } return thisNode; } void TableExprFuncNodeArray::tryToConst() { Int axarg = 1; switch (funcType()) { case TableExprFuncNode::shapeFUNC: if (operands()[0]->ndim() == 0 || operands()[0]->shape().nelements() > 0 ) { exprtype_p = Constant; } break; case TableExprFuncNode::arrfractilesFUNC: axarg = 2; case TableExprFuncNode::arrsumsFUNC: case TableExprFuncNode::arrproductsFUNC: case TableExprFuncNode::arrsumsqrsFUNC: case TableExprFuncNode::arrminsFUNC: case TableExprFuncNode::arrmaxsFUNC: case TableExprFuncNode::arrmeansFUNC: case TableExprFuncNode::arrvariancesFUNC: case TableExprFuncNode::arrstddevsFUNC: case TableExprFuncNode::arravdevsFUNC: case TableExprFuncNode::arrrmssFUNC: case TableExprFuncNode::arrmediansFUNC: case TableExprFuncNode::anysFUNC: case TableExprFuncNode::allsFUNC: case TableExprFuncNode::ntruesFUNC: case TableExprFuncNode::nfalsesFUNC: 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); 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.nelements()); for (uInt i=0; i ax(operands()[axarg]->getArrayInt(id).array()); AlwaysAssert (ax.ndim() == 1, AipsError); AlwaysAssert (ax.contiguousStorage(), AipsError); uInt ndim = ax.nelements(); 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::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::anysFUNC: { MArray arr (operands()[0]->getArrayBool(id)); return partialAnys (arr, getAxes(id, arr.ndim())); } case TableExprFuncNode::allsFUNC: { 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::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.nelements(); 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.nelements(); 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.nelements(); 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::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::boxminFUNC: { MArray arr (operands()[0]->getArrayInt(id)); return boxedMins (arr, getAxes(id, arr.ndim())); } case TableExprFuncNode::boxmaxFUNC: { MArray arr (operands()[0]->getArrayInt(id)); return boxedMaxs (arr, getAxes(id, arr.ndim())); } case TableExprFuncNode::ntruesFUNC: { 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::nfalsesFUNC: { 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::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::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); } 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.nelements(); 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.nelements(); for (size_t i=0; i= 0) { data[i] = 0; } else { data[i] = C::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.nelements(); 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::arrvariancesFUNC: { MArray arr (operands()[0]->getArrayDouble(id)); return partialVariances (arr, getAxes(id, arr.ndim())); } case TableExprFuncNode::arrstddevsFUNC: { MArray arr (operands()[0]->getArrayDouble(id)); return partialStddevs (arr, getAxes(id, arr.ndim())); } case TableExprFuncNode::arravdevsFUNC: { 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::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::runvarianceFUNC: { MArray arr (operands()[0]->getArrayDouble(id)); return slidingVariances (arr, getArrayShape(id)); } case TableExprFuncNode::runstddevFUNC: { MArray arr (operands()[0]->getArrayDouble(id)); return slidingStddevs (arr, getArrayShape(id)); } case TableExprFuncNode::runavdevFUNC: { 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::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::boxvarianceFUNC: { MArray arr (operands()[0]->getArrayDouble(id)); return boxedVariances (arr, getArrayShape(id)); } case TableExprFuncNode::boxstddevFUNC: { MArray arr (operands()[0]->getArrayDouble(id)); return boxedStddevs (arr, getArrayShape(id)); } case TableExprFuncNode::boxavdevFUNC: { 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::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::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); // 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) { if (a1.size() != a2.size()) { throw TableInvExpr ("Arguments of angdist function must have " "equal length"); } if (a1.size() %2 != 0) { throw TableInvExpr ("Arguments of angdist function must have a " "multiple of 2 values"); } 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); } // fall through if either array has size 2 } 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"); } 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)); } } /// deal with possible mask return MArray(result); } 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) { MArray arr1 (operands()[0]->getArrayDComplex(id)); Array arr2 (arr1.shape()); arr2 = operands()[1]->getDComplex(id); /// Make pow of array,scalar possible return MArray (pow(arr1.array(), arr2), arr1); } 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::arrsumsFUNC: { MArray arr (operands()[0]->getArrayDComplex(id)); return partialSums (arr, getAxes(id, arr.ndim())); } case TableExprFuncNode::arrproductsFUNC: { MArray arr (operands()[0]->getArrayDComplex(id)); return partialProducts (arr, getAxes(id, arr.ndim())); } case TableExprFuncNode::arrsumsqrsFUNC: { MArray arr (operands()[0]->getArrayDComplex(id)); return partialSums (arr*arr, getAxes(id, arr.ndim())); } 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::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::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.nelements(); size_t i; switch (funcType()) { case TableExprFuncNode::upcaseFUNC: for (i=0; igetInt (id); Int64 sz = String::npos; if (operands().size() > 2) { sz = std::max (Int64(0), operands()[2]->getInt (id)); } for (i=0; i 2) { repl = operands()[2]->getString (id); } if (operands()[1]->dataType() == TableExprNodeRep::NTString) { String patt = operands()[1]->getString(id); for (i=0; igetRegex(id).regex(); for (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(); size_t i; switch (funcType()) { case TableExprFuncNode::cmonthFUNC: for (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.nelements(); 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::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.nelements(); 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.nelements(); 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.nelements(); 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::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-2.4.1/tables/TaQL/ExprFuncNodeArray.h000066400000000000000000000150211321422335000210530ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: ExprFuncNodeArray.h 21277 2012-10-31 16:07:31Z gervandiepen $ #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 TaQLStyle&); // Destructor ~TableExprFuncNodeArray(); // Get the nodes representing an aggregate function. virtual void getAggrNodes (vector& aggr); // Get the nodes representing a table column. virtual void getColumnNodes (vector& cols); // '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; } // Link the children to the node and convert the children // to constants if possible. Also convert the node to // constant if possible. static TableExprNodeRep* fillNode (TableExprFuncNodeArray* thisNode, PtrBlock& nodes, const Block& dtypeOper); protected: // Try if the function gives a constant result. // If so, set the expression type to Constant. void tryToConst(); // Some functions to be used by TableExprNodeFuncArray. // const PtrBlock& operands() const { return node_p.operands(); } PtrBlock& rwOperands() { return node_p.rwOperands(); } TableExprFuncNode::FunctionType funcType() const { return node_p.funcType(); } NodeDataType argDataType() const { return node_p.argDataType(); } // private: // Set unit scale factor (needed for sqrt). void setScale (Double scale) { node_p.setScale (scale); } // 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 function. // 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 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); // Templated fucntion to resize/expand an array. template MArray TEFResize (const MArray& arr, const TableExprId& id); //# 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-2.4.1/tables/TaQL/ExprGroup.cc000066400000000000000000000462401321422335000176140ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: TaQLNode.h 21051 2011-04-20 11:46:29Z gervandiepen $ //# Includes #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // Aggregation and GROUPBY/HAVING clause are handled as follows: // Possibly support ROLLUP (to generate subtotals); cannot be used with median // if supported, give such columns the value 0 ("TOTAL" for string). // - an aggregation function is detected in select (GROUPBY not needed) // - detected by TaQLNode??? Probably not, otherwise cannot use MAX or so. // - thus ExprFuncNode must support gmin, etc and know it is an aggr function. // maybe have an ExprFuncNode::isAggregate, user defined funcs can be used?? // support of user defined aggr funcs makes life more complicated. // - if GROUPBY or HAVING is given, select must contain an aggr function // - TableParse will know if the SELECT and/or HAVING have an aggregate // - HAVING can refer to columns in select, but can also have its own aggr. // TableParse has map, fills a GroupKeySet object // from the GROUPBY columns and does: // for (i=0..nrow) { // GroupKeySet key = ... // akey = map.find(key); // if (akey == map.end() // akey = map.insert (key, groupaggr); // akey->second.setRow (row); // } // akey.apply (row); // } // Aggr func is allowed in select,having, not in where,join,orderby,groupby. // Is it allowed at highest level only or also in expressions? 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(); } else { TableExprAggrNodeArray* anode = dynamic_cast(node); if (anode) { itsOperand = anode->operand(); } else { TableExprUDFNode* unode = dynamic_cast(node); AlwaysAssert (unode && unode->isAggregate(), AipsError); } } } } TableExprGroupFuncBase::~TableExprGroupFuncBase() {} Bool TableExprGroupFuncBase::isLazy() const { return False; } void TableExprGroupFuncBase::finish() {} CountedPtr > 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 AipsError ("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 = new vector(); } TableExprGroupExprId::~TableExprGroupExprId() {} Bool TableExprGroupExprId::isLazy() const { return True; } void TableExprGroupExprId::apply (const TableExprId& id) { itsIds->push_back (id); } CountedPtr > 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 CountedPtr& 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-2.4.1/tables/TaQL/ExprGroup.h000066400000000000000000000753261321422335000174650ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: TaQLNode.h 21051 2011-04-20 11:46:29Z gervandiepen $ #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; Int64 itsInt64; Double itsDouble; 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(); // 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 CountedPtr > 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()); // private: // Copying is not needed, thus not allowed. TableExprGroupFuncBase (const TableExprGroupFuncBase&); TableExprGroupFuncBase& operator= (const TableExprGroupFuncBase&); protected: //# Data member TableExprNodeRep* itsNode; TableExprNodeRep* itsOperand; 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 CountedPtr > getIds() const; private: CountedPtr > 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; CountedPtr > miter; if (hasMask) { mask.resize (shp); miter = 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 act 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 act 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 act 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 act 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 act 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 act 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 act 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 act 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 act 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 act 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 act 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 act 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); // Add a function object. void add (const CountedPtr& 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: // Copying is not needed, thus not allowed. TableExprGroupFuncSet (const TableExprGroupFuncSet&); TableExprGroupFuncSet& operator= (const TableExprGroupFuncSet&); //# Data members. vector > itsFuncs; TableExprId itsId; //# row containing the non-aggregate variables }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/tables/TaQL/ExprGroupAggrFunc.cc000066400000000000000000000262541321422335000212340ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: TaQLNode.h 21051 2011-04-20 11:46:29Z gervandiepen $ //# Includes #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) : TableExprGroupFuncDouble (node), itsNr (0), itsM2 (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 - itsValue; // itsValue contains the mean itsValue += delta/itsNr; itsM2 += delta*(v-itsValue); } void TableExprGroupVarianceDouble::finish() { if (itsNr > 1) { itsValue = itsM2 / (itsNr-1); } else { itsValue = 0; } } TableExprGroupStdDevDouble::TableExprGroupStdDevDouble(TableExprNodeRep* node) : TableExprGroupVarianceDouble (node) {} 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 (uInt 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); } } } //# NAMESPACE CASACORE - END casacore-2.4.1/tables/TaQL/ExprGroupAggrFunc.h000066400000000000000000000373321321422335000210750ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: TaQLNode.h 21051 2011-04-20 11:46:29Z gervandiepen $ #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); virtual ~TableExprGroupVarianceDouble(); virtual void apply (const TableExprId& id); virtual void finish(); protected: Int64 itsNr; Double itsM2; }; // // 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); 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; }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/tables/TaQL/ExprGroupAggrFuncArray.cc000066400000000000000000001157321321422335000222330ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: TaQLNode.h 21051 2011-04-20 11:46:29Z gervandiepen $ //# 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(); // Note: itm also get incremented if there is no mask, but that is harmless. 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) : TableExprGroupFuncDouble (node), itsNr (0), itsM2 (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()) { Double meanv = mean(arr); Double m2 = 0; Int64 nr = arr.size(); if (arr.hasMask()) { nr = nfalse(arr.mask()); } if (nr > 1) { m2 = variance(arr, meanv) * (nr-1); } Double delta = meanv - itsValue; // itsValue contains the overall mean itsValue = (itsNr*itsValue + nr*meanv) / (itsNr + nr); itsM2 += (m2 + delta*delta*itsNr*nr / (itsNr + nr)); itsNr += nr; } } void TableExprGroupVarianceArrayDouble::finish() { if (itsNr > 1) { itsValue = itsM2 / (itsNr-1); } else { itsValue = 0; } } TableExprGroupStdDevArrayDouble::TableExprGroupStdDevArrayDouble(TableExprNodeRep* node) : TableExprGroupVarianceArrayDouble (node) {} 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 (uInt 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); } } 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) : TableExprGroupFuncArrayDouble (node) {} 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; itsMean.resize (arr.shape()); itsMean = 0; itsNr.resize (arr.shape()); itsNr = 0; } Array::contiter itm = itsMean.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.contiguousStorage(), AipsError); Array::contiter itv = itsValue.array().cbegin(); Array::contiter itm = itsValue.wmask().cbegin(); // Note: itm also get incremented if there is no mask, but that is harmless. for (Array::const_contiter itn = itsNr.cbegin(); itn != itsNr.cend(); ++itn, ++itv, ++itm) { if (*itn > 1) { *itv /= *itn - 1; } else { *itv = 0; *itm = True; } } } TableExprGroupStdDevsArrayDouble::TableExprGroupStdDevsArrayDouble(TableExprNodeRep* node) : TableExprGroupVariancesArrayDouble (node) {} 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.contiguousStorage(), AipsError); Array::contiter itv = itsValue.array().cbegin(); Array::contiter itm = itsValue.wmask().cbegin(); // Note: itm also get incremented if there is no mask, but that is harmless. 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); } 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-2.4.1/tables/TaQL/ExprGroupAggrFuncArray.h000066400000000000000000000750321321422335000220730ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: TaQLNode.h 21051 2011-04-20 11:46:29Z gervandiepen $ #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); virtual ~TableExprGroupVarianceArrayDouble(); virtual void apply (const TableExprId& id); virtual void finish(); protected: Int64 itsNr; Double itsM2; }; // // 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); 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 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); virtual ~TableExprGroupVariancesArrayDouble(); virtual void apply (const TableExprId& id); virtual void finish(); protected: Array itsNr; Array itsMean; }; // // 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); 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; }; // // 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-2.4.1/tables/TaQL/ExprLogicNode.cc000066400000000000000000000404601321422335000203610ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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) {} TableExprNodeEQBool::~TableExprNodeEQBool() {} Bool TableExprNodeEQBool::getBool (const TableExprId& id) { return lnode_p->getBool(id) == rnode_p->getBool(id); } TableExprNodeEQInt::TableExprNodeEQInt (const TableExprNodeRep& node) : TableExprNodeBinary (NTBool, node, OtEQ) {} TableExprNodeEQInt::~TableExprNodeEQInt() {} Bool TableExprNodeEQInt::getBool (const TableExprId& id) { return lnode_p->getInt(id) == rnode_p->getInt(id); } TableExprNodeEQDouble::TableExprNodeEQDouble (const TableExprNodeRep& node) : TableExprNodeBinary (NTBool, node, OtEQ) {} TableExprNodeEQDouble::~TableExprNodeEQDouble() {} Bool TableExprNodeEQDouble::getBool (const TableExprId& id) { return lnode_p->getDouble(id) == rnode_p->getDouble(id); } TableExprNodeEQDComplex::TableExprNodeEQDComplex (const TableExprNodeRep& node) : TableExprNodeBinary (NTBool, node, OtEQ) {} TableExprNodeEQDComplex::~TableExprNodeEQDComplex() {} Bool TableExprNodeEQDComplex::getBool (const TableExprId& id) { return lnode_p->getDComplex(id) == rnode_p->getDComplex(id); } TableExprNodeEQString::TableExprNodeEQString (const TableExprNodeRep& node) : TableExprNodeBinary (NTBool, node, OtEQ) {} TableExprNodeEQString::~TableExprNodeEQString() {} Bool TableExprNodeEQString::getBool (const TableExprId& id) { return lnode_p->getString(id) == rnode_p->getString(id); } TableExprNodeEQRegex::TableExprNodeEQRegex (const TableExprNodeRep& node) : TableExprNodeBinary (NTBool, node, OtEQ) {} TableExprNodeEQRegex::~TableExprNodeEQRegex() {} 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) {} TableExprNodeEQDate::~TableExprNodeEQDate() {} Bool TableExprNodeEQDate::getBool (const TableExprId& id) { return lnode_p->getDate(id) == rnode_p->getDate(id); } TableExprNodeNEBool::TableExprNodeNEBool (const TableExprNodeRep& node) : TableExprNodeBinary (NTBool, node, OtNE) {} TableExprNodeNEBool::~TableExprNodeNEBool() {} Bool TableExprNodeNEBool::getBool (const TableExprId& id) { return lnode_p->getBool(id) != rnode_p->getBool(id); } TableExprNodeNEInt::TableExprNodeNEInt (const TableExprNodeRep& node) : TableExprNodeBinary (NTBool, node, OtNE) {} TableExprNodeNEInt::~TableExprNodeNEInt() {} Bool TableExprNodeNEInt::getBool (const TableExprId& id) { return lnode_p->getInt(id) != rnode_p->getInt(id); } TableExprNodeNEDouble::TableExprNodeNEDouble (const TableExprNodeRep& node) : TableExprNodeBinary (NTBool, node, OtNE) {} TableExprNodeNEDouble::~TableExprNodeNEDouble() {} Bool TableExprNodeNEDouble::getBool (const TableExprId& id) { return lnode_p->getDouble(id) != rnode_p->getDouble(id); } TableExprNodeNEDComplex::TableExprNodeNEDComplex (const TableExprNodeRep& node) : TableExprNodeBinary (NTBool, node, OtNE) {} TableExprNodeNEDComplex::~TableExprNodeNEDComplex() {} Bool TableExprNodeNEDComplex::getBool (const TableExprId& id) { return lnode_p->getDComplex(id) != rnode_p->getDComplex(id); } TableExprNodeNEString::TableExprNodeNEString (const TableExprNodeRep& node) : TableExprNodeBinary (NTBool, node, OtNE) {} TableExprNodeNEString::~TableExprNodeNEString() {} Bool TableExprNodeNEString::getBool (const TableExprId& id) { return lnode_p->getString(id) != rnode_p->getString(id); } TableExprNodeNERegex::TableExprNodeNERegex (const TableExprNodeRep& node) : TableExprNodeBinary (NTBool, node, OtNE) {} TableExprNodeNERegex::~TableExprNodeNERegex() {} 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) {} TableExprNodeNEDate::~TableExprNodeNEDate() {} Bool TableExprNodeNEDate::getBool (const TableExprId& id) { return lnode_p->getDate(id) != rnode_p->getDate(id); } TableExprNodeGTInt::TableExprNodeGTInt (const TableExprNodeRep& node) : TableExprNodeBinary (NTBool, node, OtGT) {} TableExprNodeGTInt::~TableExprNodeGTInt() {} Bool TableExprNodeGTInt::getBool (const TableExprId& id) { return lnode_p->getInt(id) > rnode_p->getInt(id); } TableExprNodeGTDouble::TableExprNodeGTDouble (const TableExprNodeRep& node) : TableExprNodeBinary (NTBool, node, OtGT) {} TableExprNodeGTDouble::~TableExprNodeGTDouble() {} Bool TableExprNodeGTDouble::getBool (const TableExprId& id) { return lnode_p->getDouble(id) > rnode_p->getDouble(id); } TableExprNodeGTDComplex::TableExprNodeGTDComplex (const TableExprNodeRep& node) : TableExprNodeBinary (NTBool, node, OtGT) {} TableExprNodeGTDComplex::~TableExprNodeGTDComplex() {} Bool TableExprNodeGTDComplex::getBool (const TableExprId& id) { return lnode_p->getDComplex(id) > rnode_p->getDComplex(id); } TableExprNodeGTString::TableExprNodeGTString (const TableExprNodeRep& node) : TableExprNodeBinary (NTBool, node, OtGT) {} TableExprNodeGTString::~TableExprNodeGTString() {} Bool TableExprNodeGTString::getBool (const TableExprId& id) { return lnode_p->getString(id) > rnode_p->getString(id); } TableExprNodeGTDate::TableExprNodeGTDate (const TableExprNodeRep& node) : TableExprNodeBinary (NTBool, node, OtGT) {} TableExprNodeGTDate::~TableExprNodeGTDate() {} Bool TableExprNodeGTDate::getBool (const TableExprId& id) { return lnode_p->getDate(id) > rnode_p->getDate(id); } TableExprNodeGEInt::TableExprNodeGEInt (const TableExprNodeRep& node) : TableExprNodeBinary (NTBool, node, OtGE) {} TableExprNodeGEInt::~TableExprNodeGEInt() {} Bool TableExprNodeGEInt::getBool (const TableExprId& id) { return lnode_p->getInt(id) >= rnode_p->getInt(id); } TableExprNodeGEDouble::TableExprNodeGEDouble (const TableExprNodeRep& node) : TableExprNodeBinary (NTBool, node, OtGE) {} TableExprNodeGEDouble::~TableExprNodeGEDouble() {} Bool TableExprNodeGEDouble::getBool (const TableExprId& id) { return lnode_p->getDouble(id) >= rnode_p->getDouble(id); } TableExprNodeGEDComplex::TableExprNodeGEDComplex (const TableExprNodeRep& node) : TableExprNodeBinary (NTBool, node, OtGE) {} TableExprNodeGEDComplex::~TableExprNodeGEDComplex() {} Bool TableExprNodeGEDComplex::getBool (const TableExprId& id) { return lnode_p->getDComplex(id) >= rnode_p->getDComplex(id); } TableExprNodeGEString::TableExprNodeGEString (const TableExprNodeRep& node) : TableExprNodeBinary (NTBool, node, OtGE) {} TableExprNodeGEString::~TableExprNodeGEString() {} Bool TableExprNodeGEString::getBool (const TableExprId& id) { return lnode_p->getString(id) >= rnode_p->getString(id); } TableExprNodeGEDate::TableExprNodeGEDate (const TableExprNodeRep& node) : TableExprNodeBinary (NTBool, node, OtGE) {} TableExprNodeGEDate::~TableExprNodeGEDate() {} Bool TableExprNodeGEDate::getBool (const TableExprId& id) { return lnode_p->getDate(id) >= rnode_p->getDate(id); } TableExprNodeINInt::TableExprNodeINInt (const TableExprNodeRep& node, Bool doTracing) : TableExprNodeBinary (NTBool, node, OtIN), itsDoTracing (doTracing) {} void TableExprNodeINInt::convertConstChild() { if (rnode_p->isConstant() && rnode_p->valueType() == VTArray) { // Convert array to a set for lookup MArray values = rnode_p->getArrayInt(0); Array arr(values.array()); if (values.hasMask()) { // Remove masked elements. arr.reference (values.flatten()); } if (! arr.empty()) { itsIndexSet.clear(); itsIndexSet.insert(arr.begin(), arr.end()); } } } TableExprNodeINInt::~TableExprNodeINInt() {} Bool TableExprNodeINInt::getBool (const TableExprId& id) { Int64 val = lnode_p->getInt (id); if (itsIndexSet.size() > 0) { return itsIndexSet.find(val) != itsIndexSet.end(); } return rnode_p->hasInt (id, val); } TableExprNodeINDouble::TableExprNodeINDouble (const TableExprNodeRep& node) : TableExprNodeBinary (NTBool, node, OtIN) {} TableExprNodeINDouble::~TableExprNodeINDouble() {} Bool TableExprNodeINDouble::getBool (const TableExprId& id) { return rnode_p->hasDouble (id, lnode_p->getDouble (id)); } TableExprNodeINDComplex::TableExprNodeINDComplex (const TableExprNodeRep& node) : TableExprNodeBinary (NTBool, node, OtIN) {} TableExprNodeINDComplex::~TableExprNodeINDComplex() {} Bool TableExprNodeINDComplex::getBool (const TableExprId& id) { return rnode_p->hasDComplex (id, lnode_p->getDComplex (id)); } TableExprNodeINString::TableExprNodeINString (const TableExprNodeRep& node) : TableExprNodeBinary (NTBool, node, OtIN) {} TableExprNodeINString::~TableExprNodeINString() {} Bool TableExprNodeINString::getBool (const TableExprId& id) { return rnode_p->hasString (id, lnode_p->getString (id)); } TableExprNodeINDate::TableExprNodeINDate (const TableExprNodeRep& node) : TableExprNodeBinary (NTBool, node, OtIN) {} TableExprNodeINDate::~TableExprNodeINDate() {} Bool TableExprNodeINDate::getBool (const TableExprId& id) { return rnode_p->hasDate (id, lnode_p->getDate (id)); } TableExprNodeOR::TableExprNodeOR (const TableExprNodeRep& node) : TableExprNodeBinary (NTBool, node, OtOR) {} TableExprNodeOR::~TableExprNodeOR() {} Bool TableExprNodeOR::getBool (const TableExprId& id) { return lnode_p->getBool(id) || rnode_p->getBool(id); } TableExprNodeAND::TableExprNodeAND (const TableExprNodeRep& node) : TableExprNodeBinary (NTBool, node, OtAND) {} TableExprNodeAND::~TableExprNodeAND() {} Bool TableExprNodeAND::getBool (const TableExprId& id) { return lnode_p->getBool(id) && rnode_p->getBool(id); } TableExprNodeNOT::TableExprNodeNOT (const TableExprNodeRep& node) : TableExprNodeBinary (NTBool, node, OtNOT) {} TableExprNodeNOT::~TableExprNodeNOT() {} Bool TableExprNodeNOT::getBool (const TableExprId& id) { return ! lnode_p->getBool(id); } void TableExprNodeEQDouble::ranges (Block& blrange) { Double dval = 0; TableExprNodeRep* 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), dval, dval); } void TableExprNodeGEDouble::ranges (Block& blrange) { Double st = 0; Double end = 0; TableExprNodeRep* 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), st, end); } void TableExprNodeGTDouble::ranges (Block& blrange) { Double st = 0; Double end = 0; TableExprNodeRep* 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), 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); uInt nr=0; uInt i,j; for (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; uInt i,j; for (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(); Bool getBool (const TableExprId& id); }; // // 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(); Bool getBool (const TableExprId& id); }; // // 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(); Bool getBool (const TableExprId& id); void ranges (Block&); }; // // 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(); Bool getBool (const TableExprId& id); }; // // 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(); Bool getBool (const TableExprId& id); }; // // 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(); Bool getBool (const TableExprId& id); }; // // 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(); Bool getBool (const TableExprId& id); }; // // 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(); Bool getBool (const TableExprId& id); }; // // 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(); Bool getBool (const TableExprId& id); }; // // 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(); Bool getBool (const TableExprId& id); }; // // 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(); Bool getBool (const TableExprId& id); }; // // 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(); Bool getBool (const TableExprId& id); }; // // 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(); Bool getBool (const TableExprId& id); }; // // 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(); Bool getBool (const TableExprId& id); }; // // 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(); Bool getBool (const TableExprId& id); }; // // 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(); Bool getBool (const TableExprId& id); void ranges (Block&); }; // // 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(); Bool getBool (const TableExprId& id); }; // // 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(); Bool getBool (const TableExprId& id); }; // // 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(); Bool getBool (const TableExprId& id); }; // // 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(); Bool getBool (const TableExprId& id); }; // // 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(); Bool getBool (const TableExprId& id); void ranges (Block&); }; // // 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(); Bool getBool (const TableExprId& id); }; // // 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(); Bool getBool (const TableExprId& id); }; // // 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(); Bool getBool (const TableExprId& id); }; // // 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. // class TableExprNodeINInt : public TableExprNodeBinary { public: TableExprNodeINInt (const TableExprNodeRep&, Bool doTracing=False); virtual ~TableExprNodeINInt(); virtual void convertConstChild(); virtual Bool getBool (const TableExprId& id); private: Bool itsDoTracing; // If the right node is constant it is converted to a set std::set itsIndexSet; }; // // 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. // class TableExprNodeINDouble : public TableExprNodeBinary { public: TableExprNodeINDouble (const TableExprNodeRep&); ~TableExprNodeINDouble(); Bool getBool (const TableExprId& id); }; // // 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(); Bool getBool (const TableExprId& id); }; // // 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. // class TableExprNodeINString : public TableExprNodeBinary { public: TableExprNodeINString (const TableExprNodeRep&); ~TableExprNodeINString(); Bool getBool (const TableExprId& id); }; // // 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(); Bool getBool (const TableExprId& id); }; // // 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(); Bool getBool (const TableExprId& id); void ranges (Block&); }; // // 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(); Bool getBool (const TableExprId& id); void ranges (Block&); }; // // 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(); Bool getBool (const TableExprId& id); }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/tables/TaQL/ExprLogicNodeArray.cc000066400000000000000000000462461321422335000213700ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: ExprLogicNodeArray.cc 21262 2012-09-07 12:38:36Z gervandiepen $ #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() {} MArray TableExprNodeArrayINInt::getArrayBool (const TableExprId& id) { return rnode_p->hasArrayInt (id, lnode_p->getArrayInt (id)); } TableExprNodeArrayINDouble::TableExprNodeArrayINDouble (const TableExprNodeRep& node) : TableExprNodeArray (node, NTBool, OtIN) {} TableExprNodeArrayINDouble::~TableExprNodeArrayINDouble() {} MArray TableExprNodeArrayINDouble::getArrayBool (const TableExprId& id) { return rnode_p->hasArrayDouble (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->hasArrayDComplex (id, lnode_p->getArrayDComplex (id)); } TableExprNodeArrayINString::TableExprNodeArrayINString (const TableExprNodeRep& node) : TableExprNodeArray (node, NTBool, OtIN) {} TableExprNodeArrayINString::~TableExprNodeArrayINString() {} MArray TableExprNodeArrayINString::getArrayBool (const TableExprId& id) { return rnode_p->hasArrayString (id, lnode_p->getArrayString (id)); } TableExprNodeArrayINDate::TableExprNodeArrayINDate (const TableExprNodeRep& node) : TableExprNodeArray (node, NTBool, OtIN) {} TableExprNodeArrayINDate::~TableExprNodeArrayINDate() {} MArray TableExprNodeArrayINDate::getArrayBool (const TableExprId& id) { return rnode_p->hasArrayDate (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-2.4.1/tables/TaQL/ExprLogicNodeArray.h000066400000000000000000000641561321422335000212320ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: ExprLogicNodeArray.h 21262 2012-09-07 12:38:36Z gervandiepen $ #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. // class TableExprNodeArrayINInt : public TableExprNodeArray { public: TableExprNodeArrayINInt (const TableExprNodeRep&); ~TableExprNodeArrayINInt(); MArray getArrayBool (const TableExprId& id); }; // // 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(); MArray getArrayBool (const TableExprId& id); }; // // 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. // class TableExprNodeArrayINString : public TableExprNodeArray { public: TableExprNodeArrayINString (const TableExprNodeRep&); ~TableExprNodeArrayINString(); MArray getArrayBool (const TableExprId& id); }; // // 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(); MArray getArrayBool (const TableExprId& id); }; // // 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-2.4.1/tables/TaQL/ExprMathNode.cc000066400000000000000000000337061321422335000202220ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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() { setUnit (lnode_p->unit()); } 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-2.4.1/tables/TaQL/ExprMathNode.h000066400000000000000000000533261321422335000200640ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/tables/TaQL/ExprMathNodeArray.cc000066400000000000000000000442071321422335000212170ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: ExprMathNodeArray.cc 21262 2012-09-07 12:38:36Z gervandiepen $ #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); } void TableExprNodeArrayPlusString::concString (String* to, const String* left, Int incrLeft, const String* right, Int incrRight, uInt nr) const { String* end = to + nr; while (to < end) { *to++ = *left + *right; left += incrLeft; right += incrRight; } } 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() { setUnit (lnode_p->unit()); } 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-2.4.1/tables/TaQL/ExprMathNodeArray.h000066400000000000000000000534351321422335000210640ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: ExprMathNodeArray.h 21262 2012-09-07 12:38:36Z gervandiepen $ #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); private: // Concatenate nr arrays of strings. // The increment is 0 for a scalar value. Otherwise it is 1. void concString (String* to, const String* left, Int incrLeft, const String* right, Int incrRight, uInt nr) const; }; // // 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-2.4.1/tables/TaQL/ExprNode.cc000066400000000000000000001425641321422335000174130ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: ExprNode.cc 21277 2012-10-31 16:07:31Z gervandiepen $ #include #include #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 = new TableExprNodeConstBool (val); node_p->link(); } TableExprNode::TableExprNode (const Int64& val) { node_p = new TableExprNodeConstInt (val); node_p->link(); } TableExprNode::TableExprNode (const Int& val) { node_p = new TableExprNodeConstInt (val); node_p->link(); } TableExprNode::TableExprNode (const uInt& val) { node_p = new TableExprNodeConstInt (val); node_p->link(); } TableExprNode::TableExprNode (const Float& val) { node_p = new TableExprNodeConstDouble (Double (val)); node_p->link(); } TableExprNode::TableExprNode (const Double& val) { node_p = new TableExprNodeConstDouble (val); node_p->link(); } TableExprNode::TableExprNode (const Complex& val) { node_p = new TableExprNodeConstDComplex (DComplex(val)); node_p->link(); } TableExprNode::TableExprNode (const DComplex& val) { node_p = new TableExprNodeConstDComplex (val); node_p->link(); } TableExprNode::TableExprNode (const String& val) { node_p = new TableExprNodeConstString (val); node_p->link(); } TableExprNode::TableExprNode (const std::string& val) { node_p = new TableExprNodeConstString (String(val)); node_p->link(); } TableExprNode::TableExprNode (const char* val) { node_p = new TableExprNodeConstString (String(val)); node_p->link(); } TableExprNode::TableExprNode (const Regex& val) { node_p = new TableExprNodeConstRegex (TaqlRegex(val)); node_p->link(); } TableExprNode::TableExprNode (const StringDistance& val) { node_p = new TableExprNodeConstRegex (TaqlRegex(val)); node_p->link(); } TableExprNode::TableExprNode (const TaqlRegex& val) { node_p = new TableExprNodeConstRegex (val); node_p->link(); } TableExprNode::TableExprNode (const MVTime& val) { node_p = new TableExprNodeConstDate (val); node_p->link(); } TableExprNode::TableExprNode (const Array& val) { node_p = new TableExprNodeArrayConstBool (val); node_p->link(); } TableExprNode::TableExprNode (const Array& val) { node_p = new TableExprNodeArrayConstInt (val); node_p->link(); } TableExprNode::TableExprNode (const Array& val) { node_p = new TableExprNodeArrayConstInt (val); node_p->link(); } TableExprNode::TableExprNode (const Array& val) { node_p = new TableExprNodeArrayConstInt (val); node_p->link(); } TableExprNode::TableExprNode (const Array& val) { node_p = new TableExprNodeArrayConstInt (val); node_p->link(); } TableExprNode::TableExprNode (const Array& val) { node_p = new TableExprNodeArrayConstInt (val); node_p->link(); } TableExprNode::TableExprNode (const Array& val) { node_p = new TableExprNodeArrayConstDouble (val); node_p->link(); } TableExprNode::TableExprNode (const Array& val) { node_p = new TableExprNodeArrayConstDouble (val); node_p->link(); } TableExprNode::TableExprNode (const Array& val) { node_p = new TableExprNodeArrayConstDComplex (val); node_p->link(); } TableExprNode::TableExprNode (const Array& val) { node_p = new TableExprNodeArrayConstDComplex (val); node_p->link(); } TableExprNode::TableExprNode (const Array& val) { node_p = new TableExprNodeArrayConstString (val); node_p->link(); } TableExprNode::TableExprNode (const Array& val) { node_p = new TableExprNodeArrayConstDate (val); node_p->link(); } TableExprNode::TableExprNode (const MArray& val) { node_p = new TableExprNodeArrayConstBool (val); node_p->link(); } TableExprNode::TableExprNode (const MArray& val) { node_p = new TableExprNodeArrayConstInt (val); node_p->link(); } TableExprNode::TableExprNode (const MArray& val) { node_p = new TableExprNodeArrayConstInt (val); node_p->link(); } TableExprNode::TableExprNode (const MArray& val) { node_p = new TableExprNodeArrayConstInt (val); node_p->link(); } TableExprNode::TableExprNode (const MArray& val) { node_p = new TableExprNodeArrayConstInt (val); node_p->link(); } TableExprNode::TableExprNode (const MArray& val) { node_p = new TableExprNodeArrayConstInt (val); node_p->link(); } TableExprNode::TableExprNode (const MArray& val) { node_p = new TableExprNodeArrayConstDouble (val); node_p->link(); } TableExprNode::TableExprNode (const MArray& val) { node_p = new TableExprNodeArrayConstDouble (val); node_p->link(); } TableExprNode::TableExprNode (const MArray& val) { node_p = new TableExprNodeArrayConstDComplex (val); node_p->link(); } TableExprNode::TableExprNode (const MArray& val) { node_p = new TableExprNodeArrayConstDComplex (val); node_p->link(); } TableExprNode::TableExprNode (const MArray& val) { node_p = new TableExprNodeArrayConstString (val); node_p->link(); } TableExprNode::TableExprNode (const MArray& val) { node_p = new TableExprNodeArrayConstDate (val); node_p->link(); } TableExprNode::TableExprNode (TableExprNodeRep* node) : node_p (node->link()) {} TableExprNode::TableExprNode (const TableExprNode& node) : node_p (node.node_p) { if (node_p) { node_p->link(); } } TableExprNode& TableExprNode::operator= (const TableExprNode& that) { if (this != &that) { TableExprNodeRep::unlink (node_p); node_p = that.node_p; if (node_p) { node_p->link(); } } return *this; } //# Destructor. TableExprNode::~TableExprNode () { TableExprNodeRep::unlink (node_p); } TableExprNode operator&& (const TableExprNode& left, const TableExprNode& right) { if (left.isNull()) return right; if (right.isNull()) return left; return left.newAND (right.node_p); } TableExprNode operator|| (const TableExprNode& left, const TableExprNode& right) { if (left.isNull()) return right; if (right.isNull()) return left; return left.newOR (right.node_p); } 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.nelements() == 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; } if (node_p->table().isNull()) { return canBeConst; } return (table.nrow() == node_p->nrow()); } void TableExprNode::throwInvDT (const String& message) { throw (TableInvExpr ("invalid operand data type; " + message)); } TableExprNodeRep* TableExprNode::newPlus (TableExprNodeRep* right) const { TableExprNodeRep node = TableExprNodeBinary::getTypes (*node_p, *right, TableExprNodeRep::OtPlus); SPtrHolder tsnptr; if (node.valueType() == TableExprNodeRep::VTScalar) { switch (node.dataType()) { case TableExprNodeRep::NTInt: tsnptr.reset (new TableExprNodePlusInt (node)); break; case TableExprNodeRep::NTDouble: tsnptr.reset (new TableExprNodePlusDouble (node)); break; case TableExprNodeRep::NTComplex: tsnptr.reset (new TableExprNodePlusDComplex (node)); break; case TableExprNodeRep::NTString: tsnptr.reset (new TableExprNodePlusString (node)); break; case TableExprNodeRep::NTDate: tsnptr.reset (new TableExprNodePlusDate (node)); break; default: throwInvDT("in scalar operator+"); } }else{ switch (node.dataType()) { case TableExprNodeRep::NTInt: tsnptr.reset (new TableExprNodeArrayPlusInt (node)); break; case TableExprNodeRep::NTDouble: tsnptr.reset (new TableExprNodeArrayPlusDouble (node)); break; case TableExprNodeRep::NTComplex: tsnptr.reset (new TableExprNodeArrayPlusDComplex (node)); break; case TableExprNodeRep::NTString: tsnptr.reset (new TableExprNodeArrayPlusString (node)); break; case TableExprNodeRep::NTDate: tsnptr.reset (new TableExprNodeArrayPlusDate (node)); break; default: throwInvDT("in array operator+"); } } TableExprNodeRep* nptr = TableExprNodeBinary::fillNode (tsnptr.ptr(), node_p, right, True); tsnptr.release(); return nptr; } TableExprNodeRep* TableExprNode::newMinus (TableExprNodeRep* right) const { TableExprNodeRep node = TableExprNodeBinary::getTypes (*node_p, *right, TableExprNodeRep::OtMinus); SPtrHolder tsnptr; if (node.valueType() == TableExprNodeRep::VTScalar) { switch (node.dataType()) { case TableExprNodeRep::NTInt: tsnptr.reset (new TableExprNodeMinusInt (node)); break; case TableExprNodeRep::NTDouble: tsnptr.reset (new TableExprNodeMinusDouble (node)); break; case TableExprNodeRep::NTComplex: tsnptr.reset (new TableExprNodeMinusDComplex (node)); break; case TableExprNodeRep::NTDate: tsnptr.reset (new TableExprNodeMinusDate (node)); break; default: throwInvDT("in scalar operator-"); } }else{ switch (node.dataType()) { case TableExprNodeRep::NTInt: tsnptr.reset (new TableExprNodeArrayMinusInt (node)); break; case TableExprNodeRep::NTDouble: tsnptr.reset (new TableExprNodeArrayMinusDouble (node)); break; case TableExprNodeRep::NTComplex: tsnptr.reset (new TableExprNodeArrayMinusDComplex (node)); break; case TableExprNodeRep::NTDate: tsnptr.reset (new TableExprNodeArrayMinusDate (node)); break; default: throwInvDT("in array operator-"); } } TableExprNodeRep* nptr = TableExprNodeBinary::fillNode (tsnptr.ptr(), node_p, right, True); tsnptr.release(); return nptr; } TableExprNodeRep* TableExprNode::newTimes (TableExprNodeRep* right) const { TableExprNodeRep node = TableExprNodeBinary::getTypes (*node_p, *right, TableExprNodeRep::OtTimes); SPtrHolder tsnptr; if (node.valueType() == TableExprNodeRep::VTScalar) { switch (node.dataType()) { case TableExprNodeRep::NTInt: tsnptr.reset (new TableExprNodeTimesInt (node)); break; case TableExprNodeRep::NTDouble: tsnptr.reset (new TableExprNodeTimesDouble (node)); break; case TableExprNodeRep::NTComplex: tsnptr.reset (new TableExprNodeTimesDComplex (node)); break; default: throwInvDT("in scalar operator*"); } }else{ switch (node.dataType()) { case TableExprNodeRep::NTInt: tsnptr.reset (new TableExprNodeArrayTimesInt (node)); break; case TableExprNodeRep::NTDouble: tsnptr.reset (new TableExprNodeArrayTimesDouble (node)); break; case TableExprNodeRep::NTComplex: tsnptr.reset (new TableExprNodeArrayTimesDComplex (node)); break; default: throwInvDT("in array operator*"); } } TableExprNodeRep* nptr = TableExprNodeBinary::fillNode (tsnptr.ptr(), node_p, right, True); tsnptr.release(); return nptr; } TableExprNodeRep* TableExprNode::newDivide (TableExprNodeRep* right) const { // Note that (as in python3) integer division is exact and results in // a double. TableExprNodeRep node = TableExprNodeBinary::getTypes (*node_p, *right, TableExprNodeRep::OtDivide); SPtrHolder tsnptr; if (node.valueType() == TableExprNodeRep::VTScalar) { switch (node.dataType()) { case TableExprNodeRep::NTDouble: tsnptr.reset (new TableExprNodeDivideDouble (node)); break; case TableExprNodeRep::NTComplex: tsnptr.reset (new TableExprNodeDivideDComplex (node)); break; default: throwInvDT("in scalar operator/"); } }else{ switch (node.dataType()) { case TableExprNodeRep::NTDouble: tsnptr.reset (new TableExprNodeArrayDivideDouble (node)); break; case TableExprNodeRep::NTComplex: tsnptr.reset (new TableExprNodeArrayDivideDComplex (node)); break; default: throwInvDT("in array operator/"); } } TableExprNodeRep* nptr = TableExprNodeBinary::fillNode (tsnptr.ptr(), node_p, right, True); tsnptr.release(); return nptr; } TableExprNodeRep* TableExprNode::newModulo (TableExprNodeRep* right) const { TableExprNodeRep node = TableExprNodeBinary::getTypes (*node_p, *right, TableExprNodeRep::OtModulo); SPtrHolder tsnptr; if (node.valueType() == TableExprNodeRep::VTScalar) { switch (node.dataType()) { case TableExprNodeRep::NTInt: tsnptr.reset (new TableExprNodeModuloInt (node)); break; case TableExprNodeRep::NTDouble: tsnptr.reset (new TableExprNodeModuloDouble (node)); break; default: throwInvDT("no real operands in modulo (%)"); } }else{ switch (node.dataType()) { case TableExprNodeRep::NTInt: tsnptr.reset (new TableExprNodeArrayModuloInt (node)); break; case TableExprNodeRep::NTDouble: tsnptr.reset (new TableExprNodeArrayModuloDouble (node)); break; default: throwInvDT("no real operands in modulo (%)"); } } TableExprNodeRep* nptr = TableExprNodeBinary::fillNode (tsnptr.ptr(), node_p, right, True); tsnptr.release(); return nptr; } TableExprNodeRep* TableExprNode::newBitAnd (TableExprNodeRep* right) const { TableExprNodeRep node = TableExprNodeBinary::getTypes (*node_p, *right, TableExprNodeRep::OtBitAnd); SPtrHolder tsnptr; if (node.valueType() == TableExprNodeRep::VTScalar) { switch (node.dataType()) { case TableExprNodeRep::NTInt: tsnptr.reset (new TableExprNodeBitAndInt (node)); break; default: throwInvDT("no integer operands in bitand (&)"); } }else{ switch (node.dataType()) { case TableExprNodeRep::NTInt: tsnptr.reset (new TableExprNodeArrayBitAndInt (node)); break; default: throwInvDT("no integer operands in bitand (&)"); } } TableExprNodeRep* nptr = TableExprNodeBinary::fillNode (tsnptr.ptr(), node_p, right, True); tsnptr.release(); return nptr; } TableExprNodeRep* TableExprNode::newBitOr (TableExprNodeRep* right) const { TableExprNodeRep node = TableExprNodeBinary::getTypes (*node_p, *right, TableExprNodeRep::OtBitOr); SPtrHolder tsnptr; if (node.valueType() == TableExprNodeRep::VTScalar) { switch (node.dataType()) { case TableExprNodeRep::NTInt: tsnptr.reset (new TableExprNodeBitOrInt (node)); break; default: throwInvDT("no integer operands in bitor (|)"); } }else{ switch (node.dataType()) { case TableExprNodeRep::NTInt: tsnptr.reset (new TableExprNodeArrayBitOrInt (node)); break; default: throwInvDT("no integer operands in bitor (|)"); } } TableExprNodeRep* nptr = TableExprNodeBinary::fillNode (tsnptr.ptr(), node_p, right, True); tsnptr.release(); return nptr; } TableExprNodeRep* TableExprNode::newBitXor (TableExprNodeRep* right) const { TableExprNodeRep node = TableExprNodeBinary::getTypes (*node_p, *right, TableExprNodeRep::OtBitXor); SPtrHolder tsnptr; if (node.valueType() == TableExprNodeRep::VTScalar) { switch (node.dataType()) { case TableExprNodeRep::NTInt: tsnptr.reset (new TableExprNodeBitXorInt (node)); break; default: throwInvDT("no integer operands in bitxor (^)"); } }else{ switch (node.dataType()) { case TableExprNodeRep::NTInt: tsnptr.reset (new TableExprNodeArrayBitXorInt (node)); break; default: throwInvDT("no integer operands in bitxor (^)"); } } TableExprNodeRep* nptr = TableExprNodeBinary::fillNode (tsnptr.ptr(), node_p, right, True); tsnptr.release(); return nptr; } TableExprNodeRep* TableExprNode::newEQ (TableExprNodeRep* right) const { TableExprNodeRep node = TableExprNodeBinary::getTypes (*node_p, *right, TableExprNodeRep::OtEQ); SPtrHolder tsnptr; if (node.valueType() == TableExprNodeRep::VTScalar) { switch (node.dataType()) { case TableExprNodeRep::NTBool: tsnptr.reset (new TableExprNodeEQBool (node)); break; case TableExprNodeRep::NTInt: tsnptr.reset (new TableExprNodeEQInt (node)); break; case TableExprNodeRep::NTDouble: tsnptr.reset (new TableExprNodeEQDouble (node)); break; case TableExprNodeRep::NTComplex: tsnptr.reset (new TableExprNodeEQDComplex (node)); break; case TableExprNodeRep::NTString: tsnptr.reset (new TableExprNodeEQString (node)); break; case TableExprNodeRep::NTRegex: tsnptr.reset (new TableExprNodeEQRegex (node)); break; case TableExprNodeRep::NTDate: tsnptr.reset (new TableExprNodeEQDate (node)); break; default: throwInvDT("in scalar operator=="); } }else{ switch (node.dataType()) { case TableExprNodeRep::NTBool: tsnptr.reset (new TableExprNodeArrayEQBool (node)); break; case TableExprNodeRep::NTInt: tsnptr.reset (new TableExprNodeArrayEQInt (node)); break; case TableExprNodeRep::NTDouble: tsnptr.reset (new TableExprNodeArrayEQDouble (node)); break; case TableExprNodeRep::NTComplex: tsnptr.reset (new TableExprNodeArrayEQDComplex (node)); break; case TableExprNodeRep::NTString: tsnptr.reset (new TableExprNodeArrayEQString (node)); break; case TableExprNodeRep::NTRegex: tsnptr.reset (new TableExprNodeArrayEQRegex (node)); break; case TableExprNodeRep::NTDate: tsnptr.reset (new TableExprNodeArrayEQDate (node)); break; default: throwInvDT("in array operator=="); } } TableExprNodeRep* nptr = TableExprNodeBinary::fillNode (tsnptr.ptr(), node_p, right, True); tsnptr.release(); return nptr; } TableExprNodeRep* TableExprNode::newNE (TableExprNodeRep* right) const { TableExprNodeRep node = TableExprNodeBinary::getTypes (*node_p, *right, TableExprNodeRep::OtNE); SPtrHolder tsnptr; if (node.valueType() == TableExprNodeRep::VTScalar) { switch (node.dataType()) { case TableExprNodeRep::NTBool: tsnptr.reset (new TableExprNodeNEBool (node)); break; case TableExprNodeRep::NTInt: tsnptr.reset (new TableExprNodeNEInt (node)); break; case TableExprNodeRep::NTDouble: tsnptr.reset (new TableExprNodeNEDouble (node)); break; case TableExprNodeRep::NTComplex: tsnptr.reset (new TableExprNodeNEDComplex (node)); break; case TableExprNodeRep::NTString: tsnptr.reset (new TableExprNodeNEString (node)); break; case TableExprNodeRep::NTRegex: tsnptr.reset (new TableExprNodeNERegex (node)); break; case TableExprNodeRep::NTDate: tsnptr.reset (new TableExprNodeNEDate (node)); break; default: throwInvDT("in scalar operator<> (!=)"); } }else{ switch (node.dataType()) { case TableExprNodeRep::NTBool: tsnptr.reset (new TableExprNodeArrayNEBool (node)); break; case TableExprNodeRep::NTInt: tsnptr.reset (new TableExprNodeArrayNEInt (node)); break; case TableExprNodeRep::NTDouble: tsnptr.reset (new TableExprNodeArrayNEDouble (node)); break; case TableExprNodeRep::NTComplex: tsnptr.reset (new TableExprNodeArrayNEDComplex (node)); break; case TableExprNodeRep::NTString: tsnptr.reset (new TableExprNodeArrayNEString (node)); break; case TableExprNodeRep::NTRegex: tsnptr.reset (new TableExprNodeArrayNERegex (node)); break; case TableExprNodeRep::NTDate: tsnptr.reset (new TableExprNodeArrayNEDate (node)); break; default: throwInvDT("in array operator<> (!=)"); } } TableExprNodeRep* nptr = TableExprNodeBinary::fillNode (tsnptr.ptr(), node_p, right, True); tsnptr.release(); return nptr; } TableExprNodeRep* TableExprNode::newGT (TableExprNodeRep* right) const { TableExprNodeRep node = TableExprNodeBinary::getTypes (*node_p, *right, TableExprNodeRep::OtGT); SPtrHolder tsnptr; if (node.valueType() == TableExprNodeRep::VTScalar) { switch (node.dataType()) { case TableExprNodeRep::NTInt: tsnptr.reset (new TableExprNodeGTInt (node)); break; case TableExprNodeRep::NTDouble: tsnptr.reset (new TableExprNodeGTDouble (node)); break; case TableExprNodeRep::NTComplex: tsnptr.reset (new TableExprNodeGTDComplex (node)); break; case TableExprNodeRep::NTString: tsnptr.reset (new TableExprNodeGTString (node)); break; case TableExprNodeRep::NTDate: tsnptr.reset (new TableExprNodeGTDate (node)); break; default: throwInvDT("in scalar operator>"); } }else{ switch (node.dataType()) { case TableExprNodeRep::NTInt: tsnptr.reset (new TableExprNodeArrayGTInt (node)); break; case TableExprNodeRep::NTDouble: tsnptr.reset (new TableExprNodeArrayGTDouble (node)); break; case TableExprNodeRep::NTComplex: tsnptr.reset (new TableExprNodeArrayGTDComplex (node)); break; case TableExprNodeRep::NTString: tsnptr.reset (new TableExprNodeArrayGTString (node)); break; case TableExprNodeRep::NTDate: tsnptr.reset (new TableExprNodeArrayGTDate (node)); break; default: throwInvDT("in array operator>"); } } TableExprNodeRep* nptr = TableExprNodeBinary::fillNode (tsnptr.ptr(), node_p, right, True); tsnptr.release(); return nptr; } TableExprNodeRep* TableExprNode::newGE (TableExprNodeRep* right) const { TableExprNodeRep node = TableExprNodeBinary::getTypes (*node_p, *right, TableExprNodeRep::OtGE); SPtrHolder tsnptr; if (node.valueType() == TableExprNodeRep::VTScalar) { switch (node.dataType()) { case TableExprNodeRep::NTInt: tsnptr.reset (new TableExprNodeGEInt (node)); break; case TableExprNodeRep::NTDouble: tsnptr.reset (new TableExprNodeGEDouble (node)); break; case TableExprNodeRep::NTComplex: tsnptr.reset (new TableExprNodeGEDComplex (node)); break; case TableExprNodeRep::NTString: tsnptr.reset (new TableExprNodeGEString (node)); break; case TableExprNodeRep::NTDate: tsnptr.reset (new TableExprNodeGEDate (node)); break; default: throwInvDT("in scalar operator>="); } }else{ switch (node.dataType()) { case TableExprNodeRep::NTInt: tsnptr.reset (new TableExprNodeArrayGEInt (node)); break; case TableExprNodeRep::NTDouble: tsnptr.reset (new TableExprNodeArrayGEDouble (node)); break; case TableExprNodeRep::NTComplex: tsnptr.reset (new TableExprNodeArrayGEDComplex (node)); break; case TableExprNodeRep::NTString: tsnptr.reset (new TableExprNodeArrayGEString (node)); break; case TableExprNodeRep::NTDate: tsnptr.reset (new TableExprNodeArrayGEDate (node)); break; default: throwInvDT("in array operator>="); } } TableExprNodeRep* nptr = TableExprNodeBinary::fillNode (tsnptr.ptr(), node_p, right, True); tsnptr.release(); return nptr; } TableExprNodeRep* TableExprNode::newIN (TableExprNodeRep* right, const TaQLStyle& style) const { TableExprNodeRep::ValueType vtRight = right->valueType(); if (vtRight == TableExprNodeRep::VTScalar) { return newEQ (right); } else if (vtRight == TableExprNodeRep::VTArray) { TableExprNodeSet* set = dynamic_cast(right); if (set) { if (set->isSingle() && set->nelements() == 1 && ! set->hasArrays()) { TableExprNodeRep* snode = (*set)[0].start(); return newEQ (snode); } } else { TableExprNodeArray* arr = dynamic_cast(right); if (arr) { TableExprNodeRep* 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")); } 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"); } } TableExprNodeRep::ExprType extype = TableExprNodeRep::Variable; if (node_p->isConstant() && right->isConstant()) { extype = TableExprNodeRep::Constant; } TableExprNodeRep node (dtype, node_p->valueType(), TableExprNodeRep::OtIN, TableExprNodeRep::NoArr, extype, node_p->ndim(), node_p->shape(), node_p->table()); 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 TableExprNodeBinary::fillNode (tsnptr, node_p, right, True); } TableExprNodeRep* TableExprNode::newOR (TableExprNodeRep* right) const { TableExprNodeRep node = TableExprNodeBinary::getTypes (*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 TableExprNodeBinary::fillNode (tsnptr, node_p, right, True); } TableExprNodeRep* TableExprNode::newAND (TableExprNodeRep* right) const { TableExprNodeRep node = TableExprNodeBinary::getTypes (*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 TableExprNodeBinary::fillNode (tsnptr, node_p, right, True); } 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 TableExprNodeBinary::fillNode (tsnptr, node_p, 0, True); } 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 TableExprNodeBinary::fillNode (tsnptr, node_p, 0, True); } 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 TableExprNodeBinary::fillNode (tsnptr, node_p, 0, True); } //# 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 Table& table, const String& name, const Vector& fieldNames) { //# Get the column description. This throws an exception if //# the name is not a column. TableExprNodeRep* tsnptr = 0; const ColumnDesc& coldes = table.tableDesc().columnDesc (name); TableColumn col(table, name); if (fieldNames.nelements() > 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 = new TableExprNodeArrayColumnBool (col, table); break; case TpUChar: tsnptr = new TableExprNodeArrayColumnuChar (col, table); break; case TpShort: tsnptr = new TableExprNodeArrayColumnShort(col, table); break; case TpUShort: tsnptr = new TableExprNodeArrayColumnuShort (col, table); break; case TpInt: tsnptr = new TableExprNodeArrayColumnInt (col, table); break; case TpUInt: tsnptr = new TableExprNodeArrayColumnuInt (col, table); break; case TpFloat: tsnptr = new TableExprNodeArrayColumnFloat (col, table); break; case TpDouble: tsnptr = new TableExprNodeArrayColumnDouble (col, table); break; case TpComplex: tsnptr = new TableExprNodeArrayColumnComplex (col, table); break; case TpDComplex: tsnptr = new TableExprNodeArrayColumnDComplex (col, table); break; case TpString: tsnptr = new TableExprNodeArrayColumnString (col, table); break; default: throw (TableInvExpr (name, "unknown data type")); } } else if (coldes.isScalar()) { if (coldes.dataType() == TpRecord && fieldNames.nelements() == 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 = new TableExprNodeColumn (table, 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.nelements() - 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) { TableExprNodeRep* tsnptr = 0; 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 = new TableExprNodeConstBool (ks->asBool (name)); break; case TpString: tsnptr = new TableExprNodeConstString (ks->asString (name)); break; case TpComplex: case TpDComplex: tsnptr = new TableExprNodeConstDComplex (ks->asDComplex (name)); break; case TpFloat: case TpDouble: tsnptr = new TableExprNodeConstDouble (ks->asDouble (name)); break; case TpChar: case TpShort: case TpInt: tsnptr = new TableExprNodeConstInt (ks->asInt (name)); break; case TpUChar: case TpUShort: case TpUInt: tsnptr = new TableExprNodeConstInt (ks->asuInt (name)); break; case TpArrayBool: tsnptr = new TableExprNodeArrayConstBool (ks->asArrayBool (name)); break; case TpArrayString: tsnptr = new TableExprNodeArrayConstString (ks->asArrayString (name)); break; case TpArrayComplex: tsnptr = new TableExprNodeArrayConstDComplex (ks->asArrayComplex (name)); break; case TpArrayDComplex: tsnptr = new TableExprNodeArrayConstDComplex (ks->asArrayDComplex (name)); break; case TpArrayUChar: tsnptr = new TableExprNodeArrayConstInt (ks->asArrayuChar (name)); break; case TpArrayShort: tsnptr = new TableExprNodeArrayConstInt (ks->asArrayShort (name)); break; case TpArrayInt: tsnptr = new TableExprNodeArrayConstInt (ks->asArrayInt (name)); break; case TpArrayUInt: tsnptr = new TableExprNodeArrayConstInt (ks->asArrayuInt (name)); break; case TpArrayFloat: tsnptr = new TableExprNodeArrayConstDouble (ks->asArrayFloat (name)); break; case TpArrayDouble: tsnptr = new TableExprNodeArrayConstDouble (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, Table()); } 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, Table()); } 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, Table()); } 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, Table()); } 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, Table()); } TableExprNode TableExprNode::newFunctionNode (TableExprFuncNode::FunctionType ftype, const TableExprNodeSet& set, const Table& table, const TaQLStyle& style) { // Convert the set to a PtrBlock 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.nelements(); PtrBlock par(npar); for (uInt i=0; i(set[i].start()); } // rownrFUNC, rowidFUNC and randomFUNC are special, because they // need their own objects and the table. if (ftype == TableExprFuncNode::rownrFUNC) { TableExprNodeMulti::checkNumOfArg (0, 0, par); return newRownrNode (table, style.origin()); // first rownr is 0 or 1 } if (ftype == TableExprFuncNode::rowidFUNC) { TableExprNodeMulti::checkNumOfArg (0, 0, par); return newRowidNode (table); } if (ftype == TableExprFuncNode::randFUNC) { TableExprNodeMulti::checkNumOfArg (0, 0, par); return newRandomNode (table); } // 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; if (ftype >= TableExprFuncNode::FirstAggrFunc) { resDT = TableExprAggrNode::checkOperands (dtypeOper, resVT, ftype, par); // Create new function node and fill it. if (resVT == TableExprNodeRep::VTScalar) { TableExprFuncNode* fnode = new TableExprAggrNode (ftype, resDT, resVT, set); return TableExprFuncNode::fillNode (fnode, par, dtypeOper); } TableExprFuncNodeArray* fnode = new TableExprAggrNodeArray (ftype, resDT, resVT, set, style); return TableExprFuncNodeArray::fillNode (fnode, par, dtypeOper); } resDT = TableExprFuncNode::checkOperands (dtypeOper, resVT, vtypeOper, ftype, par); // Create new function node and fill it. // Keep it temporarily in SPtrHolder for destruction on exception. TableExprNode res; if (resVT == TableExprNodeRep::VTScalar) { TableExprFuncNode* fnode = new TableExprFuncNode (ftype, resDT, resVT, set, table); SPtrHolder fnodeHold(fnode); res = TableExprFuncNode::fillNode (fnode, par, dtypeOper); fnodeHold.release(); } else { TableExprFuncNodeArray* fnode = new TableExprFuncNodeArray (ftype, resDT, resVT, set, style); SPtrHolder fnodeHold(fnode); res = TableExprFuncNodeArray::fillNode (fnode, par, dtypeOper); fnodeHold.release(); } return res; } TableExprNode TableExprNode::newUDFNode (const String& name, const TableExprNodeSet& set, const Table& table, const TaQLStyle& style) { // Create the correct UDF object. An exception is thrown if unknown. SPtrHolder udf(UDFBase::createUDF (name, style)); // Convert the set to a PtrBlock 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.nelements(); PtrBlock par(npar); for (uInt i=0; i(set[i].start()); } udf->init (par, table, style); if (udf->ndim() == 0) { return new TableExprUDFNode (udf.transfer(), table, set); } return new TableExprUDFNodeArray (udf.transfer(), table, 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 PtrBlock 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.nelements(); PtrBlock par(npar); for (uInt i=0; i(set[i].start()); } // 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. if (resVT == TableExprNodeRep::VTScalar) { TableExprConeNode* fnode = new TableExprConeNode (ftype, resDT, set, origin); return TableExprConeNode::fillNode (fnode, par, dtypeOper); } TableExprConeNodeArray* fnode = new TableExprConeNodeArray (ftype, resDT, set, origin); return TableExprConeNodeArray::fillNode (fnode, par, dtypeOper); } 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. // Check the index bounds as far as possible. SPtrHolder inodep (new TableExprNodeIndex (indices, style)); inodep->checkIndexValues (arrayNode.node_p); TableExprNodeIndex* inode = inodep.transfer(); TableExprNodeBinary* anode = new TableExprNodeArrayPart (arrayNode.node_p, inode); return TableExprNodeBinary::fillNode (anode, arrayNode.node_p, inode, False, False); } void TableExprNode::adaptUnit (const Unit& unit) { TableExprNodeUnit::adaptUnit (node_p, unit); } TableExprNode TableExprNode::newRownrNode (const Table& table, uInt origin) { TableExprNodeRep* tsnptr = new TableExprNodeRownr (table, origin); return tsnptr; } TableExprNode TableExprNode::newRowidNode (const Table& table) { TableExprNodeRep* tsnptr = new TableExprNodeRowid (table); return tsnptr; } TableExprNode TableExprNode::newRandomNode (const Table& table) { TableExprNodeRep* tsnptr = new TableExprNodeRandom (table); return tsnptr; } 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 TpInt; 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-2.4.1/tables/TaQL/ExprNode.h000066400000000000000000002576021321422335000172550ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: ExprNode.h 21277 2012-10-31 16:07:31Z gervandiepen $ #ifndef TABLES_EXPRNODE_H #define TABLES_EXPRNODE_H //# Includes #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 TableRecord; class TableExprNodeSet; template class Block; template class Array; template class MArray; class TableExprNode; // 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); //
        // // 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, // which allows 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, Double, DComplex and String are used. // Char, uChar, Short, uShort, Int, uInt and float are converted // 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 (like 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 { //# Define the next 2 classes as friends to get the node_p. friend class TableExprNodeRep; friend class TableParse; //# 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. // Define all global functions as friends. // friend TableExprNode operator+ (const TableExprNode& left, const TableExprNode& right); friend TableExprNode operator- (const TableExprNode& left, const TableExprNode& right); friend TableExprNode operator* (const TableExprNode& left, const TableExprNode& right); friend TableExprNode operator/ (const TableExprNode& left, const TableExprNode& right); friend TableExprNode operator% (const TableExprNode& left, const TableExprNode& right); friend TableExprNode operator& (const TableExprNode& left, const TableExprNode& right); friend TableExprNode operator| (const TableExprNode& left, const TableExprNode& right); friend TableExprNode operator^ (const TableExprNode& left, const TableExprNode& right); friend TableExprNode operator== (const TableExprNode& left, const TableExprNode& right); friend TableExprNode operator!= (const TableExprNode& left, const TableExprNode& right); friend TableExprNode operator>= (const TableExprNode& left, const TableExprNode& right); friend TableExprNode operator> (const TableExprNode& left, const TableExprNode& right); friend TableExprNode operator<= (const TableExprNode& left, const TableExprNode& right); friend TableExprNode operator< (const TableExprNode& left, const TableExprNode& right); friend TableExprNode operator&& (const TableExprNode& left, const TableExprNode& right); friend TableExprNode operator|| (const TableExprNode& left, const TableExprNode& right); friend TableExprNode near (const TableExprNode& left, const TableExprNode& right); friend TableExprNode near (const TableExprNode& left, const TableExprNode& right, const TableExprNode& tolerance); friend TableExprNode nearAbs (const TableExprNode& left, const TableExprNode& right); friend TableExprNode nearAbs (const TableExprNode& left, const TableExprNode& right, const TableExprNode& tolerance); friend TableExprNode angdist (const TableExprNode& pos1, const TableExprNode& pos2); friend TableExprNode cones (const TableExprNode& sourcePos, const TableExprNode& cones); friend TableExprNode anyCone (const TableExprNode& sourcePos, const TableExprNode& cones); friend TableExprNode findCone (const TableExprNode& sourcePos, const TableExprNode& cones); friend TableExprNode cones (const TableExprNode& sourcePos, const TableExprNode& conePos, const TableExprNode& radii); friend TableExprNode anyCone (const TableExprNode& sourcePos, const TableExprNode& conePos, const TableExprNode& radii); friend TableExprNode findCone (const TableExprNode& sourcePos, const TableExprNode& conePos, const TableExprNode& radii); friend TableExprNode sin (const TableExprNode& node); friend TableExprNode sinh (const TableExprNode& node); friend TableExprNode cos (const TableExprNode& node); friend TableExprNode cosh (const TableExprNode& node); friend TableExprNode exp (const TableExprNode& node); friend TableExprNode log (const TableExprNode& node); friend TableExprNode log10 (const TableExprNode& node); friend TableExprNode pow (const TableExprNode& x, const TableExprNode& exp); friend TableExprNode square (const TableExprNode& node); friend TableExprNode cube (const TableExprNode& node); friend TableExprNode sqrt (const TableExprNode& node); friend TableExprNode norm (const TableExprNode& node); friend TableExprNode asin (const TableExprNode& node); friend TableExprNode acos (const TableExprNode& node); friend TableExprNode atan (const TableExprNode& node); friend TableExprNode atan2 (const TableExprNode& y, const TableExprNode& x); friend TableExprNode tan (const TableExprNode& node); friend TableExprNode tanh (const TableExprNode& node); friend TableExprNode sign (const TableExprNode& node); friend TableExprNode round (const TableExprNode& node); friend TableExprNode ceil (const TableExprNode& node); friend TableExprNode abs (const TableExprNode& node); friend TableExprNode floor (const TableExprNode& node); friend TableExprNode fmod (const TableExprNode& x, const TableExprNode& y); friend TableExprNode strlength (const TableExprNode& node); friend TableExprNode upcase (const TableExprNode& node); friend TableExprNode downcase (const TableExprNode& node); friend TableExprNode capitalize(const TableExprNode& node); friend TableExprNode trim (const TableExprNode& node); friend TableExprNode ltrim (const TableExprNode& node); friend TableExprNode rtrim (const TableExprNode& node); friend TableExprNode substr (const TableExprNode& str, const TableExprNode& pos); friend TableExprNode substr (const TableExprNode& str, const TableExprNode& pos, const TableExprNode& npos); friend TableExprNode replace (const TableExprNode& str, const TableExprNode& patt); friend TableExprNode replace (const TableExprNode& str, const TableExprNode& patt, const TableExprNode& repl); friend TableExprNode regex (const TableExprNode& node); friend TableExprNode pattern (const TableExprNode& node); friend TableExprNode sqlpattern(const TableExprNode& node); friend TableExprNode datetime (const TableExprNode& node); friend TableExprNode mjdtodate (const TableExprNode& node); friend TableExprNode mjd (const TableExprNode& node); friend TableExprNode date (const TableExprNode& node); friend TableExprNode year (const TableExprNode& node); friend TableExprNode month (const TableExprNode& node); friend TableExprNode day (const TableExprNode& node); friend TableExprNode cmonth (const TableExprNode& node); friend TableExprNode weekday (const TableExprNode& node); friend TableExprNode cdow (const TableExprNode& node); friend TableExprNode ctodt (const TableExprNode& node); friend TableExprNode cdate (const TableExprNode& node); friend TableExprNode ctime (const TableExprNode& node); friend TableExprNode week (const TableExprNode& node); friend TableExprNode time (const TableExprNode& node); friend TableExprNode isNaN (const TableExprNode& node); friend TableExprNode isFinite (const TableExprNode& node); friend TableExprNode min (const TableExprNode& a, const TableExprNode& b); friend TableExprNode max (const TableExprNode& a, const TableExprNode& b); friend TableExprNode conj (const TableExprNode& node); friend TableExprNode real (const TableExprNode& node); friend TableExprNode imag (const TableExprNode& node); friend TableExprNode integer (const TableExprNode& node); friend TableExprNode boolean (const TableExprNode& node); friend TableExprNode amplitude (const TableExprNode& node); friend TableExprNode phase (const TableExprNode& node); friend TableExprNode arg (const TableExprNode& node); friend TableExprNode formComplex (const TableExprNode& real, const TableExprNode& imag); friend TableExprNode formComplex (const TableExprNode& node); friend TableExprNode sum (const TableExprNode& array); friend TableExprNode product (const TableExprNode& array); friend TableExprNode sumSquare (const TableExprNode& array); friend TableExprNode min (const TableExprNode& array); friend TableExprNode max (const TableExprNode& array); friend TableExprNode mean (const TableExprNode& array); friend TableExprNode variance (const TableExprNode& array); friend TableExprNode stddev (const TableExprNode& array); friend TableExprNode avdev (const TableExprNode& array); friend TableExprNode rms (const TableExprNode& array); friend TableExprNode median (const TableExprNode& array); friend TableExprNode fractile (const TableExprNode& array, const TableExprNode& fraction); friend TableExprNode any (const TableExprNode& array); friend TableExprNode all (const TableExprNode& array); friend TableExprNode ntrue (const TableExprNode& array); friend TableExprNode nfalse (const TableExprNode& array); friend TableExprNode sums (const TableExprNode& array, const TableExprNodeSet& collapseAxes); friend TableExprNode products (const TableExprNode& array, const TableExprNodeSet& collapseAxes); friend TableExprNode sumSquares (const TableExprNode& array, const TableExprNodeSet& collapseAxes); friend TableExprNode mins (const TableExprNode& array, const TableExprNodeSet& collapseAxes); friend TableExprNode maxs (const TableExprNode& array, const TableExprNodeSet& collapseAxes); friend TableExprNode means (const TableExprNode& array, const TableExprNodeSet& collapseAxes); friend TableExprNode variances (const TableExprNode& array, const TableExprNodeSet& collapseAxes); friend TableExprNode stddevs (const TableExprNode& array, const TableExprNodeSet& collapseAxes); friend TableExprNode avdevs (const TableExprNode& array, const TableExprNodeSet& collapseAxes); friend TableExprNode rmss (const TableExprNode& array, const TableExprNodeSet& collapseAxes); friend TableExprNode medians (const TableExprNode& array, const TableExprNodeSet& collapseAxes); friend TableExprNode fractiles (const TableExprNode& array, const TableExprNode& fraction, const TableExprNodeSet& collapseAxes); friend TableExprNode anys (const TableExprNode& array, const TableExprNodeSet& collapseAxes); friend TableExprNode alls (const TableExprNode& array, const TableExprNodeSet& collapseAxes); friend TableExprNode ntrues (const TableExprNode& array, const TableExprNodeSet& collapseAxes); friend TableExprNode nfalses (const TableExprNode& array, const TableExprNodeSet& collapseAxes); friend TableExprNode runningMin (const TableExprNode& array); friend TableExprNode runningMax (const TableExprNode& array); friend TableExprNode runningMean (const TableExprNode& array); friend TableExprNode runningVariance (const TableExprNode& array); friend TableExprNode runningStddev (const TableExprNode& array); friend TableExprNode runningAvdev (const TableExprNode& array); friend TableExprNode runningRms (const TableExprNode& array); friend TableExprNode runningMedian (const TableExprNode& array); friend TableExprNode runningAny (const TableExprNode& array); friend TableExprNode runningAll (const TableExprNode& array); friend TableExprNode array (const TableExprNode& values, const TableExprNodeSet& shape); friend TableExprNode marray (const TableExprNode& array, const TableExprNode& mask); friend TableExprNode arrayData (const TableExprNode& array); friend TableExprNode arrayMask (const TableExprNode& array); friend TableExprNode arrayFlatten (const TableExprNode& array); friend TableExprNode transpose (const TableExprNode& array); friend TableExprNode transpose (const TableExprNode& array, const TableExprNodeSet& axes); friend TableExprNode diagonal (const TableExprNode& array); friend TableExprNode diagonal (const TableExprNode& array, const TableExprNode& firstAxis); friend TableExprNode diagonal (const TableExprNode& array, const TableExprNode& firstAxis, const TableExprNode& diag); friend TableExprNode isdefined (const TableExprNode& array); friend TableExprNode nelements (const TableExprNode& array); friend TableExprNode ndim (const TableExprNode& array); friend TableExprNode shape (const TableExprNode& array); friend TableExprNode iif (const TableExprNode& condition, const TableExprNode& arg1, const TableExprNode& arg2); // 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 Int64& value); TableExprNode (const Int& value); TableExprNode (const uInt& 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); 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); // // Construct a node from a node representation. TableExprNode (TableExprNodeRep*); // 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 == 0; } // 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 unit of the expression. const Unit& unit() const { return node_p->unit(); } // 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. // Zero is returned if no table is associated with it. uInt 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; 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 Vector& rownrs) const; Array getColumnuChar (const Vector& rownrs) const; Array getColumnShort (const Vector& rownrs) const; Array getColumnuShort (const Vector& rownrs) const; Array getColumnInt (const Vector& rownrs) const; Array getColumnuInt (const Vector& rownrs) const; Array getColumnFloat (const Vector& rownrs) const; Array getColumnDouble (const Vector& rownrs) const; Array getColumnComplex (const Vector& rownrs) const; Array getColumnDComplex (const Vector& rownrs) const; Array getColumnString (const Vector& rownrs) const; // // 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; // Get table. This gets the Table object to which a // TableExprNode belongs. A TableExprNode belongs to the Table to // which the column(s) used in an expression belong. Note that // all columns in an expression have to belong to the same table. const Table& table() const; // 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. // isArray indicates if the column should be an array column. static TableExprNode newColumnNode (const Table& tab, const String& name, const Vector& fieldNames); // Create a TableExprNodeConst for a table keyword // (which is handled as a constant). 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 Table& table, 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 Table& table, 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 Table& table, uInt origin); // Create rowid() function node. // Origin is always 0. static TableExprNode newRowidNode (const Table& table); // Create rand() function node. static TableExprNode newRandomNode (const Table& table); // 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 representation-object of it const TableExprNodeRep* getNodeRep() const; // Adapt the unit of the expression to the given unit (if not empty). void adaptUnit (const Unit&); private: // returns non-const pointer to the representation-object of it TableExprNodeRep* getRep(); // convert Block of TableExprNode to PtrBlock of TableExprNodeRep*. static PtrBlock convertBlockTEN (Block& nodes); // Construct a new node for the given operation. // TableExprNodeRep* newPlus (TableExprNodeRep* right) const; TableExprNodeRep* newMinus (TableExprNodeRep* right) const; TableExprNodeRep* newTimes (TableExprNodeRep* right) const; TableExprNodeRep* newDivide (TableExprNodeRep* right) const; TableExprNodeRep* newModulo (TableExprNodeRep* right) const; TableExprNodeRep* newBitAnd (TableExprNodeRep* right) const; TableExprNodeRep* newBitOr (TableExprNodeRep* right) const; TableExprNodeRep* newBitXor (TableExprNodeRep* right) const; TableExprNodeRep* newEQ (TableExprNodeRep* right) const; TableExprNodeRep* newNE (TableExprNodeRep* right) const; TableExprNodeRep* newGE (TableExprNodeRep* right) const; TableExprNodeRep* newGT (TableExprNodeRep* right) const; TableExprNodeRep* newIN (TableExprNodeRep* right, const TaQLStyle&) const; TableExprNodeRep* newOR (TableExprNodeRep* right) const; TableExprNodeRep* newAND (TableExprNodeRep* right) const; // // The actual (counted referenced) representation of a node. TableExprNodeRep* node_p; }; inline void TableExprNode::ranges (Block& blrange) { node_p->ranges (blrange); } //# Get the table from which the node is derived. inline const Table& TableExprNode::table() const { return node_p->table(); } //# 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 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 Vector& rownrs) const { return node_p->getColumnBool (rownrs); } inline Array TableExprNode::getColumnuChar (const Vector& rownrs) const { return node_p->getColumnuChar (rownrs); } inline Array TableExprNode::getColumnShort (const Vector& rownrs) const { return node_p->getColumnShort (rownrs); } inline Array TableExprNode::getColumnuShort (const Vector& rownrs) const { return node_p->getColumnuShort (rownrs); } inline Array TableExprNode::getColumnInt (const Vector& rownrs) const { return node_p->getColumnInt (rownrs); } inline Array TableExprNode::getColumnuInt (const Vector& rownrs) const { return node_p->getColumnuInt (rownrs); } inline Array TableExprNode::getColumnFloat (const Vector& rownrs) const { return node_p->getColumnFloat (rownrs); } inline Array TableExprNode::getColumnDouble (const Vector& rownrs) const { return node_p->getColumnDouble (rownrs); } inline Array TableExprNode::getColumnComplex (const Vector& rownrs) const { return node_p->getColumnComplex (rownrs); } inline Array TableExprNode::getColumnDComplex (const Vector& rownrs) const { return node_p->getColumnDComplex (rownrs); } inline Array TableExprNode::getColumnString (const Vector& rownrs) const { return node_p->getColumnString (rownrs); } inline TableExprNode operator+ (const TableExprNode& left, const TableExprNode& right) { return left.newPlus (right.node_p); } inline TableExprNode operator- (const TableExprNode& left, const TableExprNode& right) { return left.newMinus (right.node_p); } inline TableExprNode operator* (const TableExprNode& left, const TableExprNode& right) { return left.newTimes (right.node_p); } inline TableExprNode operator/ (const TableExprNode& left, const TableExprNode& right) { return left.newDivide (right.node_p); } inline TableExprNode operator% (const TableExprNode& left, const TableExprNode& right) { return left.newModulo (right.node_p); } inline TableExprNode operator& (const TableExprNode& left, const TableExprNode& right) { return left.newBitAnd (right.node_p); } inline TableExprNode operator| (const TableExprNode& left, const TableExprNode& right) { return left.newBitOr (right.node_p); } inline TableExprNode operator^ (const TableExprNode& left, const TableExprNode& right) { return left.newBitXor (right.node_p); } inline TableExprNode operator== (const TableExprNode& left, const TableExprNode& right) { return left.newEQ (right.node_p); } inline TableExprNode operator!= (const TableExprNode& left, const TableExprNode& right) { return left.newNE (right.node_p); } inline TableExprNode operator> (const TableExprNode& left, const TableExprNode& right) { return left.newGT (right.node_p); } inline TableExprNode operator>= (const TableExprNode& left, const TableExprNode& right) { return left.newGE (right.node_p); } inline TableExprNode operator<= (const TableExprNode& left, const TableExprNode& right) { return right.newGE (left.node_p); } inline TableExprNode operator< (const TableExprNode& left, const TableExprNode& right) { return right.newGT (left.node_p); } inline TableExprNode TableExprNode::in (const TableExprNode& right, const TaQLStyle& style) const { return newIN (right.node_p, 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::arrvarianceFUNC, node); } inline TableExprNode stddev (const TableExprNode& node) { return TableExprNode::newFunctionNode (TableExprFuncNode::arrstddevFUNC, 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::anyFUNC, node); } inline TableExprNode all (const TableExprNode& node) { return TableExprNode::newFunctionNode (TableExprFuncNode::allFUNC, node); } inline TableExprNode ntrue (const TableExprNode& node) { return TableExprNode::newFunctionNode (TableExprFuncNode::ntrueFUNC, node); } inline TableExprNode nfalse (const TableExprNode& node) { return TableExprNode::newFunctionNode (TableExprFuncNode::nfalseFUNC, 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::arrvariancesFUNC, array, axes); } inline TableExprNode stddevs (const TableExprNode& array, const TableExprNodeSet& axes) { return TableExprNode::newFunctionNode (TableExprFuncNode::arrstddevsFUNC, 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::anysFUNC, array, axes); } inline TableExprNode alls (const TableExprNode& array, const TableExprNodeSet& axes) { return TableExprNode::newFunctionNode (TableExprFuncNode::allsFUNC, array, axes); } inline TableExprNode ntrues (const TableExprNode& array, const TableExprNodeSet& axes) { return TableExprNode::newFunctionNode (TableExprFuncNode::ntruesFUNC, array, axes); } inline TableExprNode nfalses (const TableExprNode& array, const TableExprNodeSet& axes) { return TableExprNode::newFunctionNode (TableExprFuncNode::nfalsesFUNC, 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::runvarianceFUNC, node, halfBoxWidth); } inline TableExprNode runningStddev (const TableExprNode& node, const TableExprNodeSet& halfBoxWidth) { return TableExprNode::newFunctionNode (TableExprFuncNode::runstddevFUNC, 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::boxvarianceFUNC, node, halfBoxWidth); } inline TableExprNode boxedStddev (const TableExprNode& node, const TableExprNodeSet& halfBoxWidth) { return TableExprNode::newFunctionNode (TableExprFuncNode::boxstddevFUNC, 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); } inline void TableExprNode::show (ostream& os) const { node_p->show (os, 0); } inline const TableExprNodeRep* TableExprNode::getNodeRep() const { return node_p; } inline TableExprNodeRep* TableExprNode::getRep() { return node_p; } } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/tables/TaQL/ExprNodeArray.cc000066400000000000000000001415361321422335000204100ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: ExprNodeArray.cc 21262 2012-09-07 12:38:36Z gervandiepen $ #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, Table()) { 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, Table()) { shape_p = shape; ndim_p = shape.nelements(); if (ndim_p == 0) { ndim_p = -1; } } TableExprNodeArray::~TableExprNodeArray() {} TableExprNodeRep* TableExprNodeArray::makeConstantScalar() { if (isConstant()) { switch (dataType()) { case NTBool: { MArray arr = getArrayBool(0); if (arr.size() == 1) { return new TableExprNodeConstBool (arr.array().data()[0]); } } break; case NTInt: { MArray arr = getArrayInt(0); if (arr.size() == 1) { return new TableExprNodeConstInt (arr.array().data()[0]); } } break; case NTDouble: { MArray arr = getArrayDouble(0); if (arr.size() == 1) { return new TableExprNodeConstDouble (arr.array().data()[0]); } } break; case NTComplex: { MArray arr = getArrayDComplex(0); if (arr.size() == 1) { return new TableExprNodeConstDComplex (arr.array().data()[0]); } } break; case NTString: { MArray arr = getArrayString(0); if (arr.size() == 1) { return new TableExprNodeConstString (arr.array().data()[0]); } } break; case NTDate: { MArray arr = getArrayDate(0); if (arr.size() == 1) { return 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::hasBool (const TableExprId& id, Bool value) { return anyEQ (value, getArrayBool (id)); } Bool TableExprNodeArray::hasInt (const TableExprId& id, Int64 value) { return anyEQ (value, getArrayInt (id)); } Bool TableExprNodeArray::hasDouble (const TableExprId& id, Double value) { return anyEQ (value, getArrayDouble (id)); } Bool TableExprNodeArray::hasDComplex (const TableExprId& id, const DComplex& value) { return anyEQ (value, getArrayDComplex (id)); } Bool TableExprNodeArray::hasString (const TableExprId& id, const String& value) { return anyEQ (value, getArrayString (id)); } Bool TableExprNodeArray::hasDate (const TableExprId& id, const MVTime& value) { return anyEQ (value, getArrayDate (id)); } MArray TableExprNodeArray::hasArrayBool (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); uInt nval = value.nelements(); for (uInt i=0; i (result, value.mask()); } MArray TableExprNodeArray::hasArrayInt (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); uInt nval = value.nelements(); for (uInt i=0; i (result, value.mask()); } MArray TableExprNodeArray::hasArrayDouble (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); uInt nval = value.nelements(); for (uInt i=0; i (result, value.mask()); } MArray TableExprNodeArray::hasArrayDComplex (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); uInt nval = value.nelements(); for (uInt i=0; i (result, value.mask()); } MArray TableExprNodeArray::hasArrayString (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); uInt nval = value.nelements(); for (uInt i=0; i (result, value.mask()); } MArray TableExprNodeArray::hasArrayDate (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); uInt nval = value.nelements(); for (uInt 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::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 Table& table) : TableExprNodeArray (NTNumeric, OtColumn), selTable_p (table), 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: dtype_p = NTInt; break; case TpFloat: case TpDouble: dtype_p = NTDouble; break; default: throw (TableInvExpr (tabCol_p.columnDesc().name(), "unknown data type")); } table_p = table; 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)); } TableExprNodeArrayColumn::~TableExprNodeArrayColumn() {} void TableExprNodeArrayColumn::getColumnNodes (vector& cols) { cols.push_back (this); } 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(); selTable_p = selTable_p(rownrs); tabCol_p = TableColumn(selTable_p, 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 Table& table) : TableExprNodeArrayColumn (col, table), col_p (col) {} TableExprNodeArrayColumnBool::~TableExprNodeArrayColumnBool() {} 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 Table& table) : TableExprNodeArrayColumn (col, table), col_p (col) {} TableExprNodeArrayColumnuChar::~TableExprNodeArrayColumnuChar() {} 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 Table& table) : TableExprNodeArrayColumn (col, table), col_p (col) {} TableExprNodeArrayColumnShort::~TableExprNodeArrayColumnShort() {} 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 Table& table) : TableExprNodeArrayColumn (col, table), col_p (col) {} TableExprNodeArrayColumnuShort::~TableExprNodeArrayColumnuShort() {} 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 Table& table) : TableExprNodeArrayColumn (col, table), col_p (col) {} TableExprNodeArrayColumnInt::~TableExprNodeArrayColumnInt() {} 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 Table& table) : TableExprNodeArrayColumn (col, table), col_p (col) {} TableExprNodeArrayColumnuInt::~TableExprNodeArrayColumnuInt() {} 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); } TableExprNodeArrayColumnFloat::TableExprNodeArrayColumnFloat (const TableColumn& col, const Table& table) : TableExprNodeArrayColumn (col, table), col_p (col) {} TableExprNodeArrayColumnFloat::~TableExprNodeArrayColumnFloat() {} 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 Table& table) : TableExprNodeArrayColumn (col, table), col_p (col) {} TableExprNodeArrayColumnDouble::~TableExprNodeArrayColumnDouble() {} 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 Table& table) : TableExprNodeArrayColumn (col, table), col_p (col) {} TableExprNodeArrayColumnComplex::~TableExprNodeArrayColumnComplex() {} 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 Table& table) : TableExprNodeArrayColumn (col, table), col_p (col) {} TableExprNodeArrayColumnDComplex::~TableExprNodeArrayColumnDComplex() {} 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 Table& table) : TableExprNodeArrayColumn (col, table), col_p (col) {} TableExprNodeArrayColumnString::~TableExprNodeArrayColumnString() {} 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); } TableExprNodeIndex::~TableExprNodeIndex() {} void TableExprNodeIndex::checkIndexValues (const TableExprNodeRep* arrayNode) { uInt i; Int ndim = arrayNode->ndim(); uInt n = start_p.nelements(); // 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.nelements() > 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.nelements(); 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 :")); } TableExprNodeRep* 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.nelements(); operands_p.resize (3 * n); operands_p.set (static_cast(0)); uInt j = 0; for (uInt i=0; i(indices[inx].start()); if (rep != 0) { operands_p[j] = rep->link(); }else{ isSingle_p = False; } j++; rep = const_cast(indices[inx].end()); if (rep != 0) { operands_p[j] = rep->link(); isSingle_p = False; } j++; rep = const_cast(indices[inx].increment()); if (rep != 0) { operands_p[j] = rep->link(); 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")); } TableExprNodeRep::checkAggrFuncs (operands_p[i]); } } convertConstIndex(); if (isConstant()) { slicer_p = Slicer (start_p, end_p, incr_p, Slicer::endIsLast); } } void TableExprNodeIndex::convertConstIndex() { TableExprNodeRep* rep; uInt n = operands_p.nelements() / 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 (TableExprNodeRep* arrayNode, TableExprNodeIndex* indexNode) : TableExprNodeArray (arrayNode->dataType(), OtSlice), indexNode_p (indexNode), colNode_p (0) { checkTablePtr (indexNode); checkTablePtr (arrayNode); fillExprType (indexNode); fillExprType (arrayNode); arrNode_p = dynamic_cast(arrayNode); AlwaysAssert (arrNode_p, AipsError); // If indexing a single element, the result is a scalar. if (indexNode->isSingle()) { vtype_p = VTScalar; ndim_p = 0; } else if (indexNode->isConstant()) { // Otherwise if the index node is constant, it may be possible // to determine the resulting shape. const Slicer& slicer = indexNode->getSlicer(0); // If all slicer lengths are defined, that is the resulting shape. if (slicer.isFixed()) { shape_p = slicer.length(); ndim_p = shape_p.nelements(); }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.nelements() > 0) { IPosition blc,trc,inc; shape_p = slicer.inferShapeFromSource (arrshp, blc, trc, inc); ndim_p = shape_p.nelements(); } } } if (indexNode->isConstant()) { // If the constant child is an ArrayColumn, things can be // improved in getColumnXXX. colNode_p = dynamic_cast(arrayNode); } } TableExprNodeArrayPart::~TableExprNodeArrayPart() {} void TableExprNodeArrayPart::show (ostream& os, uInt indent) const { TableExprNodeRep::show (os, indent); os << "array: "; lnode_p->show (os, indent+2); os << "index: "; indexNode_p->show (os, indent+2); } Bool TableExprNodeArrayPart::getColumnDataType (DataType& dt) const { //# Return data type of column if constant index. if (indexNode_p->isConstant()) { return lnode_p->getColumnDataType (dt); } return False; } //# Note that all following casts are perfectly safe. Bool TableExprNodeArrayPart::getBool (const TableExprId& id) { DebugAssert (valueType() == VTScalar, AipsError); return arrNode_p->getElemBool (id, indexNode_p->getSlicer(id)); } Int64 TableExprNodeArrayPart::getInt (const TableExprId& id) { DebugAssert (valueType() == VTScalar, AipsError); return arrNode_p->getElemInt (id, indexNode_p->getSlicer(id)); } Double TableExprNodeArrayPart::getDouble (const TableExprId& id) { DebugAssert (valueType() == VTScalar, AipsError); return arrNode_p->getElemDouble (id, indexNode_p->getSlicer(id)); } DComplex TableExprNodeArrayPart::getDComplex (const TableExprId& id) { DebugAssert (valueType() == VTScalar, AipsError); return arrNode_p->getElemDComplex (id, indexNode_p->getSlicer(id)); } String TableExprNodeArrayPart::getString (const TableExprId& id) { DebugAssert (valueType() == VTScalar, AipsError); return arrNode_p->getElemString (id, indexNode_p->getSlicer(id)); } MVTime TableExprNodeArrayPart::getDate (const TableExprId& id) { DebugAssert (valueType() == VTScalar, AipsError); return arrNode_p->getElemDate (id, indexNode_p->getSlicer(id)); } MArray TableExprNodeArrayPart::getArrayBool (const TableExprId& id) { DebugAssert (valueType() == VTArray, AipsError); return arrNode_p->getSliceBool (id, indexNode_p->getSlicer(id)); } MArray TableExprNodeArrayPart::getArrayInt (const TableExprId& id) { DebugAssert (valueType() == VTArray, AipsError); return arrNode_p->getSliceInt (id, indexNode_p->getSlicer(id)); } MArray TableExprNodeArrayPart::getArrayDouble (const TableExprId& id) { DebugAssert (valueType() == VTArray, AipsError); return arrNode_p->getSliceDouble (id, indexNode_p->getSlicer(id)); } MArray TableExprNodeArrayPart::getArrayDComplex (const TableExprId& id) { DebugAssert (valueType() == VTArray, AipsError); return arrNode_p->getSliceDComplex (id, indexNode_p->getSlicer(id)); } MArray TableExprNodeArrayPart::getArrayString (const TableExprId& id) { DebugAssert (valueType() == VTArray, AipsError); return arrNode_p->getSliceString (id, indexNode_p->getSlicer(id)); } MArray TableExprNodeArrayPart::getArrayDate (const TableExprId& id) { DebugAssert (valueType() == VTArray, AipsError); return arrNode_p->getSliceDate (id, indexNode_p->getSlicer(id)); } Array TableExprNodeArrayPart::getColumnBool (const Vector& rownrs) { if (colNode_p == 0) { return TableExprNodeRep::getColumnBool (rownrs); } return colNode_p->getElemColumnBool (rownrs, indexNode_p->getSlicer(0)); } Array TableExprNodeArrayPart::getColumnuChar (const Vector& rownrs) { if (colNode_p == 0) { return TableExprNodeRep::getColumnuChar (rownrs); } return colNode_p->getElemColumnuChar (rownrs, indexNode_p->getSlicer(0)); } Array TableExprNodeArrayPart::getColumnShort (const Vector& rownrs) { if (colNode_p == 0) { return TableExprNodeRep::getColumnShort (rownrs); } return colNode_p->getElemColumnShort (rownrs, indexNode_p->getSlicer(0)); } Array TableExprNodeArrayPart::getColumnuShort (const Vector& rownrs) { if (colNode_p == 0) { return TableExprNodeRep::getColumnuShort (rownrs); } return colNode_p->getElemColumnuShort (rownrs, indexNode_p->getSlicer(0)); } Array TableExprNodeArrayPart::getColumnInt (const Vector& rownrs) { if (colNode_p == 0) { return TableExprNodeRep::getColumnInt (rownrs); } return colNode_p->getElemColumnInt (rownrs, indexNode_p->getSlicer(0)); } Array TableExprNodeArrayPart::getColumnuInt (const Vector& rownrs) { if (colNode_p == 0) { return TableExprNodeRep::getColumnuInt (rownrs); } return colNode_p->getElemColumnuInt (rownrs, indexNode_p->getSlicer(0)); } Array TableExprNodeArrayPart::getColumnFloat (const Vector& rownrs) { if (colNode_p == 0) { return TableExprNodeRep::getColumnFloat (rownrs); } return colNode_p->getElemColumnFloat (rownrs, indexNode_p->getSlicer(0)); } Array TableExprNodeArrayPart::getColumnDouble (const Vector& rownrs) { if (colNode_p == 0) { return TableExprNodeRep::getColumnDouble (rownrs); } return colNode_p->getElemColumnDouble (rownrs, indexNode_p->getSlicer(0)); } Array TableExprNodeArrayPart::getColumnComplex (const Vector& rownrs) { if (colNode_p == 0) { return TableExprNodeRep::getColumnComplex (rownrs); } return colNode_p->getElemColumnComplex (rownrs, indexNode_p->getSlicer(0)); } Array TableExprNodeArrayPart::getColumnDComplex (const Vector& rownrs) { if (colNode_p == 0) { return TableExprNodeRep::getColumnDComplex (rownrs); } return colNode_p->getElemColumnDComplex (rownrs, indexNode_p->getSlicer(0)); } Array TableExprNodeArrayPart::getColumnString (const Vector& rownrs) { if (colNode_p == 0) { return TableExprNodeRep::getColumnString (rownrs); } return colNode_p->getElemColumnString (rownrs, indexNode_p->getSlicer(0)); } } //# NAMESPACE CASACORE - END casacore-2.4.1/tables/TaQL/ExprNodeArray.h000066400000000000000000000700641321422335000202470ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: ExprNodeArray.h 21262 2012-09-07 12:38:36Z gervandiepen $ #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(); // Turn a constant array with one element into a scalar. // It returns a zero pointer if not possible. // The default implementation returns 0. virtual TableExprNodeRep* 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. virtual const IPosition& getShape (const TableExprId& id); // The default implementation of getArrayDouble does // getArrayInt and converts the result. virtual MArray getArrayDouble (const TableExprId& id); // The default implementation of getArrayDComplex does // getArrayDouble and converts the result. virtual MArray getArrayDComplex (const TableExprId& id); // Does a value occur in the set? // virtual Bool hasBool (const TableExprId& id, Bool value); virtual Bool hasInt (const TableExprId& id, Int64 value); virtual Bool hasDouble (const TableExprId& id, Double value); virtual Bool hasDComplex (const TableExprId& id, const DComplex& value); virtual Bool hasString (const TableExprId& id, const String& value); virtual Bool hasDate (const TableExprId& id, const MVTime& value); virtual MArray hasArrayBool (const TableExprId& id, const MArray& value); virtual MArray hasArrayInt (const TableExprId& id, const MArray& value); virtual MArray hasArrayDouble (const TableExprId& id, const MArray& value); virtual MArray hasArrayDComplex (const TableExprId& id, const MArray& value); virtual MArray hasArrayString (const TableExprId& id, const MArray& value); virtual MArray hasArrayDate (const TableExprId& id, const MArray& value); // // 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 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 Table& table); ~TableExprNodeArrayColumn(); // This node represents a table column. virtual void getColumnNodes (vector& cols); // Do not apply the selection. virtual void disableApplySelection(); // Re-create the column object for a selection of rows. virtual void applySelection (const Vector& rownrs); // Get the TableColumn object. const TableColumn& getColumn() const; // Get the shape of the array in the given row. virtual const IPosition& getShape (const TableExprId& id); // Is the value in the given row defined? virtual Bool isDefined (const TableExprId& id); // Get the data type of this column. // It returns with a True status. virtual Bool getColumnDataType (DataType&) const; protected: Table selTable_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 Table&); ~TableExprNodeArrayColumnBool(); // Re-create the column object for a selection of rows. virtual void applySelection (const Vector& rownrs); virtual Bool getElemBool (const TableExprId& id, const Slicer& index); virtual MArray getArrayBool (const TableExprId& id); virtual MArray getSliceBool (const TableExprId& id, const Slicer&); virtual Array getElemColumnBool (const Vector& rownrs, const Slicer&); 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 Table&); ~TableExprNodeArrayColumnuChar(); // Re-create the column object for a selection of rows. virtual void applySelection (const Vector& rownrs); virtual Int64 getElemInt (const TableExprId& id, const Slicer& index); virtual MArray getArrayInt (const TableExprId& id); virtual MArray getSliceInt (const TableExprId& id, const Slicer&); virtual Array getElemColumnuChar (const Vector& rownrs, const Slicer&); 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 Table&); ~TableExprNodeArrayColumnShort(); // Re-create the column object for a selection of rows. virtual void applySelection (const Vector& rownrs); virtual Int64 getElemInt (const TableExprId& id, const Slicer& index); virtual MArray getArrayInt (const TableExprId& id); virtual MArray getSliceInt (const TableExprId& id, const Slicer&); virtual Array getElemColumnShort (const Vector& rownrs, const Slicer&); 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 Table&); ~TableExprNodeArrayColumnuShort(); // Re-create the column object for a selection of rows. virtual void applySelection (const Vector& rownrs); virtual Int64 getElemInt (const TableExprId& id, const Slicer& index); virtual MArray getArrayInt (const TableExprId& id); virtual MArray getSliceInt (const TableExprId& id, const Slicer&); virtual Array getElemColumnuShort (const Vector& rownrs, const Slicer&); 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 Table&); ~TableExprNodeArrayColumnInt(); // Re-create the column object for a selection of rows. virtual void applySelection (const Vector& rownrs); virtual Int64 getElemInt (const TableExprId& id, const Slicer& index); virtual MArray getArrayInt (const TableExprId& id); virtual MArray getSliceInt (const TableExprId& id, const Slicer&); virtual Array getElemColumnInt (const Vector& rownrs, const Slicer&); 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 Table&); ~TableExprNodeArrayColumnuInt(); // Re-create the column object for a selection of rows. virtual void applySelection (const Vector& rownrs); virtual Int64 getElemInt (const TableExprId& id, const Slicer& index); virtual MArray getArrayInt (const TableExprId& id); virtual MArray getSliceInt (const TableExprId& id, const Slicer&); virtual Array getElemColumnuInt (const Vector& rownrs, const Slicer&); 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 Table&); ~TableExprNodeArrayColumnFloat(); // Re-create the column object for a selection of rows. virtual void applySelection (const Vector& rownrs); virtual Double getElemDouble (const TableExprId& id, const Slicer& index); virtual MArray getArrayDouble (const TableExprId& id); virtual MArray getSliceDouble (const TableExprId& id, const Slicer&); virtual Array getElemColumnFloat (const Vector& rownrs, const Slicer&); 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 Table&); ~TableExprNodeArrayColumnDouble(); // Re-create the column object for a selection of rows. virtual void applySelection (const Vector& rownrs); virtual Double getElemDouble (const TableExprId& id, const Slicer& index); virtual MArray getArrayDouble (const TableExprId& id); virtual MArray getSliceDouble (const TableExprId& id, const Slicer&); virtual Array getElemColumnDouble (const Vector& rownrs, const Slicer&); 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 Table&); ~TableExprNodeArrayColumnComplex(); // Re-create the column object for a selection of rows. virtual void applySelection (const Vector& rownrs); virtual DComplex getElemDComplex (const TableExprId& id, const Slicer& index); virtual MArray getArrayDComplex (const TableExprId& id); virtual MArray getSliceDComplex (const TableExprId& id, const Slicer&); virtual Array getElemColumnComplex (const Vector& rownrs, const Slicer&); 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 Table&); ~TableExprNodeArrayColumnDComplex(); // Re-create the column object for a selection of rows. virtual void applySelection (const Vector& rownrs); virtual DComplex getElemDComplex (const TableExprId& id, const Slicer& index); virtual MArray getArrayDComplex (const TableExprId& id); virtual MArray getSliceDComplex (const TableExprId& id, const Slicer&); virtual Array getElemColumnDComplex (const Vector& rownrs, const Slicer&); 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 Table&); ~TableExprNodeArrayColumnString(); // Re-create the column object for a selection of rows. virtual void applySelection (const Vector& rownrs); virtual String getElemString (const TableExprId& id, const Slicer& index); virtual MArray getArrayString (const TableExprId& id); virtual MArray getSliceString (const TableExprId& id, const Slicer&); virtual Array getElemColumnString (const Vector& rownrs, const Slicer&); 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 virtual ~TableExprNodeIndex(); // 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 TableExprNodeRep* 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 TableExprNodeRep* 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 (TableExprNodeRep* arrayNode, TableExprNodeIndex*); ~TableExprNodeArrayPart(); // Show the node. void show (ostream& os, uInt indent) const; 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); MVTime getDate (const TableExprId& id); MArray getArrayBool (const TableExprId& id); MArray getArrayInt (const TableExprId& id); MArray getArrayDouble (const TableExprId& id); MArray getArrayDComplex (const TableExprId& id); MArray getArrayString (const TableExprId& id); MArray getArrayDate (const TableExprId& id); // 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; Array getColumnBool (const Vector& rownrs); Array getColumnuChar (const Vector& rownrs); Array getColumnShort (const Vector& rownrs); Array getColumnuShort (const Vector& rownrs); Array getColumnInt (const Vector& rownrs); Array getColumnuInt (const Vector& rownrs); Array getColumnFloat (const Vector& rownrs); Array getColumnDouble (const Vector& rownrs); Array getColumnComplex (const Vector& rownrs); Array getColumnDComplex (const Vector& rownrs); Array getColumnString (const Vector& rownrs); // 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* indexNode_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 indexNode_p; } inline const TableExprNodeArrayColumn* TableExprNodeArrayPart::getColumnNode() const { return colNode_p; } } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/tables/TaQL/ExprNodeRecord.cc000066400000000000000000000237351321422335000205500ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: ExprNodeRecord.cc 21262 2012-09-07 12:38:36Z gervandiepen $ #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN TableExprNodeRecordField::TableExprNodeRecordField (DataType dtype, const Block& fieldNumbers) : TableExprNodeBinary (NTNumeric, VTScalar, OtField, Table()), 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: throw (AipsError ("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: throw (AipsError ("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-2.4.1/tables/TaQL/ExprNodeRecord.h000066400000000000000000000116021321422335000204000ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: ExprNodeRecord.h 21262 2012-09-07 12:38:36Z gervandiepen $ #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, uInt and float are converted //# 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-2.4.1/tables/TaQL/ExprNodeRep.cc000066400000000000000000001074251321422335000200570ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: ExprNodeRep.cc 21262 2012-09-07 12:38:36Z gervandiepen $ #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // The constructor to be used by the derived classes. TableExprNodeRep::TableExprNodeRep (NodeDataType dtype, ValueType vtype, OperType optype, const Table& table) : count_p (0), table_p (table), dtype_p (dtype), vtype_p (vtype), optype_p (optype), argtype_p (NoArr), exprtype_p (Variable), ndim_p (0) { if (table.isNull()) { exprtype_p = Constant; } } TableExprNodeRep::TableExprNodeRep (NodeDataType dtype, ValueType vtype, OperType optype, ArgType argtype, ExprType exprtype, Int ndim, const IPosition& shape, const Table& table) : count_p (0), table_p (table), dtype_p (dtype), vtype_p (vtype), optype_p (optype), argtype_p (argtype), exprtype_p (exprtype), ndim_p (ndim), shape_p (shape) {} TableExprNodeRep::TableExprNodeRep (const TableExprNodeRep& that) : count_p (0), table_p (that.table_p), dtype_p (that.dtype_p), vtype_p (that.vtype_p), optype_p (that.optype_p), argtype_p (that.argtype_p), exprtype_p (that.exprtype_p), ndim_p (that.ndim_p), shape_p (that.shape_p), unit_p (that.unit_p) {} TableExprNodeRep::~TableExprNodeRep () {} void TableExprNodeRep::unlink (TableExprNodeRep* node) { if (node != 0) { if (--node->count_p == 0) { delete node; } } } void TableExprNodeRep::show (ostream& os, uInt indent) const { for (uInt i=0; i&) {} void TableExprNodeRep::getAggrNodes (vector&) {} void TableExprNodeRep::getColumnNodes (vector&) {} void TableExprNodeRep::checkAggrFuncs (const TableExprNodeRep* node) { vector aggr; if (node) { const_cast(node)->getAggrNodes (aggr); if (! aggr.empty()) { throw TableInvExpr("Invalid use of an aggregate function " "(only use in SELECT or HAVING clause)"); } } } 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 table used in the expression. uInt TableExprNodeRep::nrow() const { if (exprtype_p == Constant) { return 1; } if (table_p.isNull()) { return 1; // for calc expressions } return table_p.nrow(); } void TableExprNodeRep::convertConstChild() {} void TableExprNodeRep::checkTablePtr (Table& table, const TableExprNodeRep* node) { if (node != 0) { if (table.isNull() || table.nrow() == 0) { table = node->table(); }else{ if (!(node->table().isNull() || node->table().nrow() == 0) && node->table().nrow() != table.nrow()) { throw (TableInvExpr ("expression uses differently sized tables")); } } } } void TableExprNodeRep::fillExprType (ExprType& type, const TableExprNodeRep* node) { if (node != 0 && !node->isConstant()) { type = 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.nelements() != 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::hasBool (const TableExprId& id, Bool value) { return (value == getBool(id)); } Bool TableExprNodeRep::hasInt (const TableExprId& id, Int64 value) { return (value == getInt(id)); } Bool TableExprNodeRep::hasDouble (const TableExprId& id, Double value) { return (value == getDouble(id)); } Bool TableExprNodeRep::hasDComplex (const TableExprId& id, const DComplex& value) { return (value == getDComplex(id)); } Bool TableExprNodeRep::hasString (const TableExprId& id, const String& value) { return (value == getString(id)); } Bool TableExprNodeRep::hasDate (const TableExprId& id, const MVTime& value) { return (value == getDate(id)); } MArray TableExprNodeRep::hasArrayBool (const TableExprId& id, const MArray& value) { return (getBool(id) == value); } MArray TableExprNodeRep::hasArrayInt (const TableExprId& id, const MArray& value) { return (getInt(id) == value); } MArray TableExprNodeRep::hasArrayDouble (const TableExprId& id, const MArray& value) { return (getDouble(id) == value); } MArray TableExprNodeRep::hasArrayDComplex (const TableExprId& id, const MArray& value) { return (getDComplex(id) == value); } MArray TableExprNodeRep::hasArrayString (const TableExprId& id, const MArray& value) { return (getString(id) == value); } MArray TableExprNodeRep::hasArrayDate (const TableExprId& id, const MArray& value) { return (getDate(id) == value); } Array TableExprNodeRep::getColumnBool (const Vector& rownrs) { TableExprId id; uInt nrrow = rownrs.size(); Vector vec (nrrow); for (uInt 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; uInt nrrow = rownrs.size(); Vector vec (nrrow); for (uInt i=0; i TableExprNodeRep::getColumnuInt (const Vector&) { TableExprNode::throwInvDT ("(getColumnuInt not implemented)"); return Array(); } // Array TableExprNodeRep::getColumnInt64 // (const Vector& rownrs) // { // TableExprId id; // uInt nrrow = rownrs.size(); // Vector vec (nrrow); // for (uInt i=0; i TableExprNodeRep::getColumnFloat (const Vector&) { TableExprNode::throwInvDT ("(getColumnFloat not implemented)"); return Array(); } Array TableExprNodeRep::getColumnDouble (const Vector& rownrs) { TableExprId id; uInt nrrow = rownrs.size(); Vector vec (nrrow); for (uInt i=0; i TableExprNodeRep::getColumnComplex (const Vector&) { TableExprNode::throwInvDT ("(getColumnComplex not implemented)"); return Array(); } Array TableExprNodeRep::getColumnDComplex (const Vector& rownrs) { TableExprId id; uInt nrrow = rownrs.size(); Vector vec (nrrow); for (uInt i=0; i TableExprNodeRep::getColumnString (const Vector& rownrs) { TableExprId id; uInt nrrow = rownrs.size(); Vector vec (nrrow); for (uInt i=0; ioperType() != OtOR && thisNode->operType() != OtAND) { return this; } // Determine if and which node is a constant. // Exit if no constant. TableExprNodeRep** constNode = &lnode_p; TableExprNodeRep** 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. (**otherNode).count_p++; delete thisNode; (**otherNode).count_p--; 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; // In the calling routine something like the following has to be done. TableExprNodeRep* node = shortcutAndOr(); if (node != thisNode) { node->count_p++; // prevent child from being deleted by delete thisNode delete thisNode; node->count_p--; } } #endif TableExprNodeRep* TableExprNodeRep::convertNode (TableExprNodeRep* thisNode, Bool convertConstType) { // If the expression is not constant, try to convert the type // of a constant child to the other child's type. if (! thisNode->isConstant()) { if (convertConstType) { thisNode->convertConstChild(); } return thisNode; } // Evaluate the constant subexpression and replace the node. TableExprNodeRep* newNode = 0; if (thisNode->valueType() == VTScalar) { switch (thisNode->dataType()) { case NTBool: newNode = new TableExprNodeConstBool (thisNode->getBool (0)); break; case NTInt: newNode = new TableExprNodeConstInt (thisNode->getInt (0)); break; case NTDouble: newNode = new TableExprNodeConstDouble (thisNode->getDouble (0)); break; case NTComplex: newNode = new TableExprNodeConstDComplex (thisNode->getDComplex(0)); break; case NTString: newNode = new TableExprNodeConstString (thisNode->getString (0)); break; case NTRegex: newNode = new TableExprNodeConstRegex (thisNode->getRegex (0)); break; case NTDate: newNode = new TableExprNodeConstDate (thisNode->getDate (0)); break; default: TableExprNode::throwInvDT ("in convertNode"); // should never occur } }else{ switch (thisNode->dataType()) { case NTBool: newNode = new TableExprNodeArrayConstBool (thisNode->getArrayBool (0)); break; case NTInt: newNode = new TableExprNodeArrayConstInt (thisNode->getArrayInt (0)); break; case NTDouble: newNode = new TableExprNodeArrayConstDouble (thisNode->getArrayDouble (0)); break; case NTComplex: newNode = new TableExprNodeArrayConstDComplex (thisNode->getArrayDComplex (0)); break; case NTString: newNode = new TableExprNodeArrayConstString (thisNode->getArrayString (0)); break; case NTDate: newNode = new TableExprNodeArrayConstDate (thisNode->getArrayDate (0)); break; default: TableExprNode::throwInvDT ("in convertNode"); // should never occur } } newNode->setUnit (thisNode->unit()); delete thisNode; return newNode; } TableExprNodeRep* TableExprNodeRep::getRep (TableExprNode& node) { return node.getRep(); } // ------------------------------ // TableExprNodeBinary functions // ------------------------------ TableExprNodeBinary::TableExprNodeBinary (NodeDataType tp, ValueType vtype, OperType oper, const Table& table) : TableExprNodeRep (tp, vtype, oper, table), lnode_p (0), rnode_p (0) {} TableExprNodeBinary::TableExprNodeBinary (NodeDataType tp, const TableExprNodeRep& that, OperType oper) : TableExprNodeRep (that), lnode_p (0), rnode_p (0) { dtype_p = tp; optype_p = oper; } TableExprNodeBinary::~TableExprNodeBinary() { unlink (lnode_p); unlink (rnode_p); } 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::getAggrNodes (vector& aggr) { if (lnode_p) { lnode_p->getAggrNodes (aggr); } if (rnode_p) { rnode_p->getAggrNodes (aggr); } } void TableExprNodeBinary::getColumnNodes (vector& cols) { if (lnode_p) { lnode_p->getColumnNodes (cols); } if (rnode_p) { rnode_p->getColumnNodes (cols); } } // 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::getTypes (const TableExprNodeRep& left, const TableExprNodeRep& 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.nelements(); rightNdim = rightShape.nelements(); 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. Table table = left.table(); checkTablePtr (table, &right); return TableExprNodeRep (dtype, vtype, opt, atype, extype, ndim, shape, table); } // Fill the child pointers of a node. // Also reduce the tree if possible by combining constants. // When only one of the nodes is a constant, convert its type if // it does not match the other one. TableExprNodeRep* TableExprNodeBinary::fillNode (TableExprNodeBinary* thisNode, TableExprNodeRep* left, TableExprNodeRep* right, Bool convertConstType, Bool adaptDataType) { // Fill the children and link to them. // If needed, change the children to get matching data types. thisNode->lnode_p = left->link(); if (right != 0) { thisNode->rnode_p = right->link(); if (adaptDataType) { // Adapt data types as needed. // NTRegex will always be placed in the right node if (left->dataType() == NTRegex) { thisNode->lnode_p = right; thisNode->rnode_p = left; } // If expression with date and double or string, convert to date if (left->dataType() == NTDate) { if (right->dataType() == NTString) { TableExprNode dNode = datetime (right); unlink (right); thisNode->rnode_p = getRep(dNode)->link(); } else if (right->dataType() == NTDouble || right->dataType() == NTInt) { TableExprNode dNode = mjdtodate (right); unlink (right); thisNode->rnode_p = getRep(dNode)->link(); } } if (right->dataType() == NTDate) { if (left->dataType() == NTString) { TableExprNode dNode = datetime (left); unlink (left); thisNode->lnode_p = getRep(dNode)->link(); } else if (left->dataType() == NTDouble || left->dataType() == NTInt) { TableExprNode dNode = mjdtodate (left); unlink (left); thisNode->lnode_p = getRep(dNode)->link(); } } // date-date results in double, so convert if needed. if (thisNode->dataType() == NTDouble) { if (left->dataType() == NTDate) { TableExprNode dNode = mjd (left); unlink (left); thisNode->lnode_p = getRep(dNode)->link(); } if (right->dataType() == NTDate) { TableExprNode dNode = mjd (right); unlink (right); thisNode->rnode_p = getRep(dNode)->link(); } } } } // Check and adapt units. thisNode->handleUnits(); return convertNode (thisNode, convertConstType); } const Unit& TableExprNodeBinary::makeEqualUnits (TableExprNodeRep* left, TableExprNodeRep*& right) { // The first real unit is chosen as the result unit. const Unit* unit = &(left->unit()); if (right != 0) { 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 if not bool. if (dataType() != NTBool) { setUnit (resUnit); } } void TableExprNodeBinary::convertConstChild() { // 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 == 0 || lnode_p->dataType() == rnode_p->dataType()) { return; } // Determine if and which node is a constant. TableExprNodeRep** constNode = &lnode_p; TableExprNodeRep** 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 TableExprNodeRep* newNode; if (vtype == VTScalar) { if (newType == NTDouble) { newNode = new TableExprNodeConstDouble ((**constNode).getDouble(0)); } else { newNode = new TableExprNodeConstDComplex ((**constNode).getDouble(0)); } }else{ if (newType == NTDouble) { newNode = new TableExprNodeArrayConstDouble ((**constNode).getArrayDouble(0)); } else { newNode = new TableExprNodeArrayConstDComplex ((**constNode).getArrayDouble(0)); } } newNode->setUnit ((**constNode).unit()); unlink (*constNode); *constNode = newNode->link(); } // ---------------------------- // TableExprNodeMulti functions // ---------------------------- TableExprNodeMulti::TableExprNodeMulti (NodeDataType tp, ValueType vtype, OperType oper, const TableExprNodeRep& source) : TableExprNodeRep (tp, vtype, oper, source.table()), operands_p (0) { exprtype_p = source.exprType(); } TableExprNodeMulti::~TableExprNodeMulti() { for (uInt i=0; ishow (os, indent+2); } } } void TableExprNodeMulti::getAggrNodes (vector& aggr) { for (uInt j=0; jgetAggrNodes (aggr); } } } void TableExprNodeMulti::getColumnNodes (vector& cols) { for (uInt j=0; jgetColumnNodes (cols); } } } CountedPtr TableExprNodeRep::makeGroupAggrFunc() { throw AipsError ("TableExprNodeRep::makeGroupAggrFunc should not be called"); } Bool TableExprNodeRep::isLazyAggregate() const { return True; } uInt TableExprNodeMulti::checkNumOfArg (uInt low, uInt high, const PtrBlock& nodes) { if (nodes.nelements() < low) { throw (TableInvExpr("too few function arguments")); } else if (nodes.nelements() > high) { throw (TableInvExpr("too many function arguments")); } return nodes.nelements(); } TableExprNodeRep::NodeDataType TableExprNodeMulti::checkDT (Block& dtypeOper, NodeDataType dtIn, NodeDataType dtOut, const PtrBlock& nodes) { uInt nelem = nodes.nelements(); 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; } } uInt i; 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 (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 (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 (i=0; idataType() != dtIn) { if (dtIn == NTDate) { if (nodes[i]->dataType() != NTString && nodes[i]->dataType() != NTDouble) { 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-2.4.1/tables/TaQL/ExprNodeRep.h000066400000000000000000000643641321422335000177250ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: ExprNodeRep.h 21262 2012-09-07 12:38:36Z gervandiepen $ #ifndef TABLES_EXPRNODEREP_H #define TABLES_EXPRNODEREP_H //# Includes #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; // // 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; }; // // 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, const Table& table); // This constructor is called from the derived TableExprNodeRep. TableExprNodeRep (NodeDataType, ValueType, OperType, const Table&); // Copy constructor. TableExprNodeRep (const TableExprNodeRep&); // The destructor deletes all the underlying TableExprNode objects. virtual ~TableExprNodeRep(); // Link to this object, i.e. increase its reference count. TableExprNodeRep* link(); // Unlink from the given object. // If its reference count is zero, delete it. static void unlink (TableExprNodeRep*); // 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; // Throw an exception if an aggregate function is used in // the expression node. static void checkAggrFuncs (const TableExprNodeRep* node); // Get the nodes representing an aggregate function. virtual void getAggrNodes (vector& aggr); // Get the nodes representing a table column. virtual void getColumnNodes (vector& cols); // Create the correct immediate aggregate function object. // The default implementation throws an exception, because it should // only be called for TableExprAggrNode(Array). virtual CountedPtr 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 value occur in an array or set? // The default implementation tests if it is in an array. // virtual Bool hasBool (const TableExprId& id, Bool value); virtual Bool hasInt (const TableExprId& id, Int64 value); virtual Bool hasDouble (const TableExprId& id, Double value); virtual Bool hasDComplex (const TableExprId& id, const DComplex& value); virtual Bool hasString (const TableExprId& id, const String& value); virtual Bool hasDate (const TableExprId& id, const MVTime& value); virtual MArray hasArrayBool (const TableExprId& id, const MArray& value); virtual MArray hasArrayInt (const TableExprId& id, const MArray& value); virtual MArray hasArrayDouble (const TableExprId& id, const MArray& value); virtual MArray hasArrayDComplex (const TableExprId& id, const MArray& value); virtual MArray hasArrayString (const TableExprId& id, const MArray& value); virtual MArray hasArrayDate (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. // Zero is returned if no table is associated with it. uInt nrow() const; // 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 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 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; // Get table. This gets the Table object to which a // TableExprNode belongs. A TableExprNode belongs to the Table to // which the first column used in an expression belongs. // Table& table(); const Table& table() const; // // 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: uInt count_p; //# Reference count Table table_p; //# Table from which node is "derived" 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 // Get the shape for the given row. virtual const IPosition& getShape (const TableExprId& id); // Get pointer to REPresentation object. // This is used by derived classes. static TableExprNodeRep* getRep (TableExprNode&); // When 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. // The default implementation does nothing. virtual void convertConstChild(); // Check if this node uses the same table pointer. // Fill the Table object if it is still null. // void checkTablePtr (const TableExprNodeRep* node); static void checkTablePtr (Table& table, const TableExprNodeRep* node); // // Set expression type to Variable if node is Variable. // void fillExprType (const TableExprNodeRep* node); static void fillExprType (ExprType&, const TableExprNodeRep* node); // // When 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 when appropriate. static TableExprNodeRep* convertNode (TableExprNodeRep* thisNode, Bool convertConstType); private: // A copy of a TableExprNodeRep cannot be made. TableExprNodeRep& operator= (const TableExprNodeRep&); }; // // 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 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, const Table&); TableExprNodeBinary (NodeDataType, const TableExprNodeRep&, OperType); // Destructor virtual ~TableExprNodeBinary(); // Show the expression tree. virtual void show (ostream&, uInt indent) const; // Get the nodes representing an aggregate function. virtual void getAggrNodes (vector& aggr); // Get the nodes representing a table column. virtual void getColumnNodes (vector& cols); // 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 getTypes (const TableExprNodeRep& left, const TableExprNodeRep& right, OperType operType); // Link the children to the node and convert the children // to constants if needed and possible. Also convert the node to // constant if possible. // The operand data types can be adapted as needed. static TableExprNodeRep* fillNode (TableExprNodeBinary* thisNode, TableExprNodeRep* left, TableExprNodeRep* right, Bool convertConstType, Bool adaptDataType=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(); // When 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 convertConstChild(); // Get the child nodes. // const TableExprNodeRep* getLeftChild() const { return lnode_p; } const TableExprNodeRep* getRightChild() const { return rnode_p; } // protected: // Make the units equal. // Replace the right node if needed. static const Unit& makeEqualUnits (TableExprNodeRep* left, TableExprNodeRep*& right); TableExprNodeRep* lnode_p; //# left operand TableExprNodeRep* 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 virtual ~TableExprNodeMulti(); // Show the expression tree. virtual void show (ostream&, uInt indent) const; // Get the nodes representing an aggregate function. virtual void getAggrNodes (vector& aggr); // Get the nodes representing a table column. virtual void getColumnNodes (vector& cols); // 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 PtrBlock& nodes); // Get the child nodes. const PtrBlock& 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). static NodeDataType checkDT (Block& dtypeOper, NodeDataType dtIn, NodeDataType dtOut, const PtrBlock& nodes); protected: PtrBlock 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; } //# 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; } //# Get the table from which the node is derived. inline Table& TableExprNodeRep::table() { return table_p; } inline const Table& TableExprNodeRep::table() const { return table_p; } inline void TableExprNodeRep::checkTablePtr (const TableExprNodeRep* node) { checkTablePtr (table_p, node); } inline void TableExprNodeRep::fillExprType (const TableExprNodeRep* node) { fillExprType (exprtype_p, node); } //# Link to the node. inline TableExprNodeRep* TableExprNodeRep::link() { count_p++; return this; } } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/tables/TaQL/ExprNodeSet.cc000066400000000000000000001364421321422335000200650ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: ExprNodeSet.cc 21262 2012-09-07 12:38:36Z gervandiepen $ #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN TableExprNodeSetElem::TableExprNodeSetElem (const TableExprNode& value) : TableExprNodeRep (NTDouble, VTSetElem, OtUndef, Table()), itsStart (0), itsEnd (0), itsIncr (0), itsEndExcl (False), itsLeftClosed (True), itsRightClosed (True), itsDiscrete (True), itsSingle (True) { //# Note that the TableExprNode copy ctor is needed to get rid of const. TableExprNode tmp(value); itsStart = getRep(tmp)->link(); dtype_p = itsStart->dataType(); try { setUnit (itsStart->unit()); checkTable(); } catch (const std::exception&) { // Unlink, because destructor is not called if constructor // throws an exception. unlink (itsStart); throw; } } TableExprNodeSetElem::TableExprNodeSetElem (const TableExprNode* start, const TableExprNode* end, const TableExprNode* incr, Bool isEndExcl) : TableExprNodeRep (NTDouble, VTSetElem, OtUndef, Table()), itsStart (0), itsEnd (0), itsIncr (0), itsEndExcl (isEndExcl), itsLeftClosed (True), itsRightClosed (True), itsDiscrete (True), itsSingle (False) { // Link to the nodes and determine the data types. //# Note that the TableExprNode copy ctor is needed to get rid of const. Bool isScalar = True; NodeDataType dts = NTInt; if (start != 0) { TableExprNode tmp(*start); itsStart = getRep(tmp)->link(); dts = itsStart->dataType(); isScalar = isScalar && start->isScalar(); } NodeDataType dte = dts; if (end != 0) { TableExprNode tmp(*end); itsEnd = getRep(tmp)->link(); dte = itsEnd->dataType(); isScalar = isScalar && end->isScalar(); } NodeDataType dti = NTInt; if (incr != 0) { TableExprNode tmp(*incr); itsIncr = getRep(tmp)->link(); 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; try { 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; checkTable(); } catch (const std::exception&) { // Unlink, because destructor is not called if constructor // throws an exception. unlink (itsStart); unlink (itsEnd); unlink (itsIncr); throw; } } TableExprNodeSetElem::TableExprNodeSetElem (Bool isLeftClosed, const TableExprNode& start, const TableExprNode& end, Bool isRightClosed) : TableExprNodeRep (NTDouble, VTSetElem, OtUndef, Table()) { setup (isLeftClosed, &start, &end, isRightClosed); } TableExprNodeSetElem::TableExprNodeSetElem (Bool isLeftClosed, const TableExprNode& start) : TableExprNodeRep (NTDouble, VTSetElem, OtUndef, Table()) { setup (isLeftClosed, &start, 0, False); } TableExprNodeSetElem::TableExprNodeSetElem (const TableExprNode& end, Bool isRightClosed) : TableExprNodeRep (NTDouble, VTSetElem, OtUndef, Table()) { setup (False, 0, &end, isRightClosed); } TableExprNodeSetElem::TableExprNodeSetElem (const TableExprNodeSetElem& that) : TableExprNodeRep (that), itsStart (that.itsStart), itsEnd (that.itsEnd), itsIncr (that.itsIncr), itsEndExcl (that.itsEndExcl), itsLeftClosed (that.itsLeftClosed), itsRightClosed (that.itsRightClosed), itsDiscrete (that.itsDiscrete), itsSingle (that.itsSingle) { if (itsStart != 0) { itsStart->link(); } if (itsEnd != 0) { itsEnd->link(); } if (itsIncr != 0) { itsIncr->link(); } } TableExprNodeSetElem::TableExprNodeSetElem (const TableExprNodeSetElem& that, TableExprNodeRep* start, TableExprNodeRep* end, TableExprNodeRep* incr) : TableExprNodeRep (that.dataType(), VTSetElem, OtUndef, Table()), itsStart (start), itsEnd (end), itsIncr (incr), itsEndExcl (that.itsEndExcl), itsLeftClosed (that.itsLeftClosed), itsRightClosed (that.itsRightClosed), itsDiscrete (that.itsDiscrete), itsSingle (that.itsSingle) { if (itsStart != 0) { itsStart->link(); } if (itsEnd != 0) { itsEnd->link(); } if (itsIncr != 0) { itsIncr->link(); } try { TableExprNodeUnit::adaptUnits (itsStart, itsEnd, itsIncr); } catch (const std::exception&) { // Unlink, because destructor is not called if constructor // throws an exception. unlink (itsStart); unlink (itsEnd); unlink (itsIncr); throw; } } TableExprNodeSetElem::~TableExprNodeSetElem() { unlink (itsStart); unlink (itsEnd); unlink (itsIncr); } void TableExprNodeSetElem::setup (Bool isLeftClosed, const TableExprNode* start, const TableExprNode* end, Bool isRightClosed) { itsStart = 0; itsEnd = 0; itsIncr = 0; itsEndExcl = False; itsLeftClosed = isLeftClosed; itsRightClosed = isRightClosed; itsDiscrete = False; itsSingle = False; Bool isScalar = True; //# Note that the TableExprNode copy ctor is needed to get rid of const. if (start != 0) { TableExprNode tmp(*start); itsStart = getRep(tmp)->link(); isScalar = isScalar && start->isScalar(); // Get data type; integer continuous interval is always double dtype_p = itsStart->dataType(); if (dtype_p == NTInt) { dtype_p = NTDouble; } } try { if (end != 0) { TableExprNode tmp(*end); itsEnd = getRep(tmp)->link(); isScalar = isScalar && end->isScalar(); NodeDataType etype = itsEnd->dataType(); if (etype == NTInt) { etype = NTDouble; } if (start != 0 && 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 != NTInt && 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)); checkTable(); } catch (const std::exception&) { // Unlink, because destructor is not called if constructor // throws an exception. unlink (itsStart); unlink (itsEnd); unlink (itsIncr); throw; } } void TableExprNodeSetElem::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 TableExprNodeSetElem::show (ostream& os, uInt indent) const { TableExprNodeRep::show (os, indent); if (itsStart != 0) { os << "start: "; itsStart->show (os, indent+2); } if (itsEnd != 0) { os << "end: "; itsEnd->show (os, indent+2); } if (itsIncr != 0) { os << "incr: "; itsIncr->show (os, indent+2); } } void TableExprNodeSetElem::getAggrNodes (vector& aggr) { if (itsStart != 0) { itsStart->getAggrNodes (aggr); } if (itsEnd != 0) { itsEnd->getAggrNodes (aggr); } if (itsIncr != 0) { itsIncr->getAggrNodes (aggr); } } void TableExprNodeSetElem::getColumnNodes (vector& cols) { if (itsStart != 0) { itsStart->getColumnNodes (cols); } if (itsEnd != 0) { itsEnd->getColumnNodes (cols); } if (itsIncr != 0) { itsIncr->getColumnNodes (cols); } } void TableExprNodeSetElem::checkTable() { table_p = Table(); checkTablePtr (itsStart); checkTablePtr (itsEnd); checkTablePtr (itsIncr); exprtype_p = Constant; fillExprType (itsStart); fillExprType (itsEnd); fillExprType (itsIncr); } TableExprNodeSetElem* TableExprNodeSetElem::evaluate (const TableExprId& id) const { TableExprNodeRep* start = 0; TableExprNodeRep* end = 0; TableExprNodeRep* incr = 0; switch (dataType()) { case NTBool: if (itsStart != 0) { start = new TableExprNodeConstBool (itsStart->getBool (id)); } break; case NTInt: if (itsStart != 0) { start = new TableExprNodeConstInt (itsStart->getInt (id)); } if (itsEnd != 0) { end = new TableExprNodeConstInt (itsEnd->getInt (id)); } if (itsIncr != 0) { incr = new TableExprNodeConstInt (itsIncr->getInt (id)); } break; case NTDouble: if (itsStart != 0) { start = new TableExprNodeConstDouble (itsStart->getDouble (id)); } if (itsEnd != 0) { end = new TableExprNodeConstDouble (itsEnd->getDouble (id)); } if (itsIncr != 0) { incr = new TableExprNodeConstDouble (itsIncr->getDouble (id)); } break; case NTComplex: if (itsStart != 0) { start = new TableExprNodeConstDComplex (itsStart->getDComplex (id)); } break; case NTString: if (itsStart != 0) { start = new TableExprNodeConstString (itsStart->getString (id)); } if (itsEnd != 0) { end = new TableExprNodeConstString (itsEnd->getString (id)); } break; case NTDate: if (itsStart != 0) { start = new TableExprNodeConstDate (itsStart->getDate (id)); } if (itsEnd != 0) { end = new TableExprNodeConstDate (itsEnd->getDate (id)); } if (itsIncr != 0) { incr = new TableExprNodeConstDouble (itsIncr->getDouble (id)); } break; default: TableExprNode::throwInvDT ("TableExprNodeSetElem::evaluate"); } return new TableExprNodeSetElem (*this, start, end, incr); } void TableExprNodeSetElem::fillVector (Vector& vec, Int64& cnt, const TableExprId& id) const { DebugAssert (itsSingle, AipsError); Int64 n = vec.nelements(); if (n < cnt+1) { vec.resize (cnt+64, True); } vec(cnt++) = itsStart->getBool (id); } void TableExprNodeSetElem::fillVector (Vector& vec, Int64& cnt, const TableExprId& id) const { DebugAssert (itsDiscrete, AipsError); Int64 start = itsStart==0 ? 0 : itsStart->getInt (id); Int64 end = itsEnd==0 ? start : itsEnd->getInt (id); Int64 incr = itsIncr==0 ? 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.nelements(); if (n < cnt+nval) { vec.resize (cnt+max(64,nval), True); } for (Int64 i=0; i& vec, Int64& cnt, const TableExprId& id) const { DebugAssert (itsDiscrete, AipsError); Double start = itsStart==0 ? 0 : itsStart->getDouble (id); Double end = itsEnd==0 ? start : itsEnd->getDouble (id); Double incr = itsIncr==0 ? 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.nelements(); if (n < cnt+nval) { vec.resize (cnt+max(64,nval), True); } for (Int64 i=0; i& vec, Int64& cnt, const TableExprId& id) const { DebugAssert (itsSingle, AipsError); Int64 n = vec.nelements(); if (n < cnt+1) { vec.resize (cnt+64, True); } vec(cnt++) = itsStart->getDComplex (id); } void TableExprNodeSetElem::fillVector (Vector& vec, Int64& cnt, const TableExprId& id) const { DebugAssert (itsSingle, AipsError); Int64 n = vec.nelements(); if (n < cnt+1) { vec.resize (cnt+64, True); } vec(cnt++) = itsStart->getString (id); } void TableExprNodeSetElem::fillVector (Vector& vec, Int64& cnt, const TableExprId& id) const { DebugAssert (itsDiscrete, AipsError); Double start = itsStart==0 ? 0 : Double(itsStart->getDate (id)); Double end = itsEnd==0 ? start : Double(itsEnd->getDate (id)); Double incr = itsIncr==0 ? 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.nelements(); if (n < cnt+nval) { vec.resize (cnt+max(64,nval), True); } for (Int64 i=0; igetBool (id); Bool* lastVal = match + nval; while (match < lastVal) { if (*value == start) { *match = True; } value++; match++; } } void TableExprNodeSetElem::matchInt (Bool* match, const Int64* value, uInt nval, const TableExprId& id) const { Int64 start = itsStart==0 ? 0 : itsStart->getInt (id); Int64 end = itsEnd==0 ? start : itsEnd->getInt (id); Int64 incr = itsIncr==0 ? 1 : itsIncr->getInt (id); if (incr == 0) { throw TableInvExpr("Increment in a range must be non-zero"); } Bool* lastVal = match + nval; if (itsSingle) { while (match < lastVal) { if (*value == start) { *match = True; } value++; match++; } } else if (itsDiscrete) { end -= start; if (itsEndExcl) { end -= 1; } while (match < lastVal) { Int64 tmp = *value - start; if (incr > 0) { if (tmp >= 0 && (itsEnd == 0 || tmp <= end)) { if (tmp%incr == 0) { *match = True; } } } else { if (tmp <= 0 && (itsEnd == 0 || tmp >= end)) { if (tmp%incr == 0) { *match = True; } } } value++; match++; } }else{ while (match < lastVal) { Int64 tmp = *value; if ((itsStart == 0 || tmp > start || (itsLeftClosed && tmp == start)) && (itsEnd == 0 || tmp < end || (itsRightClosed && tmp == end))) { *match = True; } value++; match++; } } } void TableExprNodeSetElem::matchDouble (Bool* match, const Double* value, uInt nval, const TableExprId& id) const { Double start = itsStart==0 ? 0 : itsStart->getDouble (id); Double end = itsEnd==0 ? start : itsEnd->getDouble (id); Double incr = itsIncr==0 ? 1 : itsIncr->getDouble (id); if (incr == 0) { throw TableInvExpr("Increment in a range must be non-zero"); } Bool* lastVal = match + nval; if (itsSingle) { while (match < lastVal) { if (*value == start) { *match = True; } value++; match++; } } else if (itsDiscrete) { end -= start; while (match < lastVal) { Double tmp = *value - start; if (incr > 0) { if (tmp >= 0 && (itsEnd == 0 || tmp < end || (!itsEndExcl && tmp==end))) { if (near(tmp, incr*Int64(tmp/incr + 0.5))) { *match = True; } } } else { if (tmp <= 0 && (itsEnd == 0 || tmp > end || (!itsEndExcl && tmp==end))) { if (near(tmp, incr*Int64(tmp/incr + 0.5))) { *match = True; } } } value++; match++; } }else{ while (match < lastVal) { Double tmp = *value; if ((itsStart == 0 || tmp > start || (itsLeftClosed && tmp == start)) && (itsEnd == 0 || tmp < end || (itsRightClosed && tmp == end))) { *match = True; } value++; match++; } } } void TableExprNodeSetElem::matchDComplex (Bool* match, const DComplex* value, uInt nval, const TableExprId& id) const { DebugAssert (itsSingle, AipsError); DComplex start = itsStart->getDComplex (id); Bool* lastVal = match + nval; while (match < lastVal) { if (*value == start) { *match = True; } value++; match++; } } void TableExprNodeSetElem::matchString (Bool* match, const String* value, uInt nval, const TableExprId& id) const { String start; if (itsStart != 0) { start = itsStart->getString (id); } String end; if (itsEnd != 0) { end = itsEnd->getString (id); } Bool* lastVal = match + nval; if (itsDiscrete) { DebugAssert (itsSingle, AipsError); while (match < lastVal) { if (*value == start) { *match = True; } value++; match++; } }else{ while (match < lastVal) { if ((itsStart == 0 || *value > start || (itsLeftClosed && *value == start)) && (itsEnd == 0 || *value < end || (itsRightClosed && *value == end))) { *match = True; } value++; match++; } } } void TableExprNodeSetElem::matchDate (Bool* match, const MVTime* value, uInt nval, const TableExprId& id) const { Double start = itsStart==0 ? 0 : Double(itsStart->getDate (id)); Double end = itsEnd==0 ? start : Double(itsEnd->getDate (id)); Double incr = itsIncr==0 ? 1 : itsIncr->getDouble (id); if (incr == 0) { throw TableInvExpr("Increment in a range must be non-zero"); } Bool* lastVal = match + nval; if (itsSingle) { while (match < lastVal) { if (Double(*value) == start) { *match = True; } value++; match++; } } else if (itsDiscrete) { end -= start; while (match < lastVal) { Double tmp = Double(*value) - start; if (incr > 0) { if (tmp >= 0 && (itsEnd == 0 || tmp < end || (!itsEndExcl && tmp==end))) { if (near(tmp, incr*Int64(tmp/incr + 0.5))) { *match = True; } } } else { if (tmp <= 0 && (itsEnd == 0 || tmp > end || (!itsEndExcl && tmp==end))) { if (near(tmp, incr*Int64(tmp/incr + 0.5))) { *match = True; } } } value++; match++; } }else{ while (match < lastVal) { Double tmp = *value; if ((itsStart == 0 || tmp > start || (itsLeftClosed && tmp == start)) && (itsEnd == 0 || tmp < end || (itsRightClosed && tmp == end))) { *match = True; } value++; match++; } } } TableExprNodeSet::TableExprNodeSet() : TableExprNodeRep (NTNumeric, VTSet, OtUndef, Table()), itsSingle (True), itsDiscrete (True), itsBounded (True), itsCheckTypes (True), itsAllIntervals (False), itsFindFunc (0) {} TableExprNodeSet::TableExprNodeSet (const IPosition& indices) : TableExprNodeRep (NTInt, VTSet, OtUndef, Table()), itsSingle (True), itsDiscrete (True), itsBounded (True), itsCheckTypes (False), itsAllIntervals (False), itsFindFunc (0) { uInt n = indices.nelements(); itsElems.resize (n); for (uInt i=0; i& rownrs, const TableExprNodeSet& set) : TableExprNodeRep (set.dataType(), VTSet, OtUndef, Table()), itsElems (rownrs.size() * set.nelements()), itsSingle (set.isSingle()), itsDiscrete (set.isDiscrete()), itsBounded (set.isBounded()), itsCheckTypes (False), itsAllIntervals (False), itsFindFunc (0) { // Fill in all values. uInt nrel = set.nelements(); for (uInt i=0; i 1 && !isSingle() && !isDiscrete()) { if (set.dataType() == NTInt) { combineIntIntervals(); } else if (set.dataType() == NTDouble) { combineDoubleIntervals(); } else if (set.dataType() == NTDate) { combineDateIntervals(); } } setUnit (set.unit()); } TableExprNodeSet::TableExprNodeSet (const TableExprNodeSet& that) : TableExprNodeRep (that), itsSingle (that.itsSingle), itsDiscrete (that.itsDiscrete), itsBounded (that.itsBounded), itsCheckTypes (that.itsCheckTypes), itsAllIntervals (that.itsAllIntervals), itsStart (that.itsStart), itsEnd (that.itsEnd), itsFindFunc (that.itsFindFunc) { uInt n = that.itsElems.size(); itsElems.resize (n); for (uInt i=0; i 0, AipsError); // Make an id (with an arbitrary row number) for the gets. TableExprId id(0); std::vector elems(1); TableExprNodeSetElem& elem = *(itsElems[0]); if (elem.start() == 0) { // No start value, so only the highest end value is relevant. // Make a single interval with the used open/closed-ness. Int64 val = elem.end()->getInt(id); for (uInt i=1; iend()->getInt(id); if (valn > val) { val = valn; } } elems[0] = new TableExprNodeSetElem(TableExprNode(val), elem.isRightClosed()); } else if (elem.end() == 0) { // No end value, so only the lowest start value is relevant. Int64 val = elem.start()->getInt(id); for (uInt i=1; istart()->getInt(id); if (valn < val) { val = valn; } } elems[0] = new TableExprNodeSetElem(elem.isLeftClosed(), TableExprNode(val)); } else { // The intervals contain both a start and an end value. // Make the block large enough for all possible intervals. elems.resize (itsElems.size()); uInt nelem = 0; // Get all start values and sort them (indirectly) in ascending order. Block vals(itsElems.size()); for (uInt i=0; istart()->getInt(id); } Vector index; GenSortIndirect::sort (index, vals, vals.nelements()); // Get the start and end value of first interval in sorted list. Int64 stval = vals[index[0]]; Int64 endval = itsElems[index[0]]->end()->getInt(id); // Loop through the next intervals and combine if possible. for (uInt i=1; iend()->getInt(id); // Combine intervals if they overlap. // They do if the next interval starts before end of this one // or if starting at the end and one side of the interval is closed. if (st2 < endval || (st2 == endval && (elem.isLeftClosed() || elem.isRightClosed()))) { // Overlap; update end if higher. if (end2 > endval) { endval = end2; } } else { // No overlap, so create the interval found and start a new one. elems[nelem++] = new TableExprNodeSetElem(elem.isLeftClosed(), stval, endval, elem.isRightClosed()); stval = st2; endval = end2; } } // Create the last interval and resize the array to #intervals found. elems[nelem++] = new TableExprNodeSetElem(elem.isLeftClosed(), stval, endval, elem.isRightClosed()); elems.resize (nelem); // Store the values in a start and an end array. itsStart.resize (nelem); itsEnd.resize (nelem); for (uInt i=0; istart()->getInt(id); itsEnd[i] = elems[i]->end()->getInt(id); } setFindFunc (elem.isLeftClosed(), elem.isRightClosed()); itsAllIntervals = True; } // Delete all existing intervals and replace by new ones. deleteElems(); itsElems = elems; } void TableExprNodeSet::combineDoubleIntervals() { DebugAssert (itsElems.size() > 0, AipsError); // Make an id (with an arbitrary row number) for the gets. TableExprId id(0); std::vector elems(1); TableExprNodeSetElem& elem = *(itsElems[0]); if (elem.start() == 0) { // No start value, so only the highest end value is relevant. // Make a single interval with the used open/closed-ness. Double val = elem.end()->getDouble(id); for (uInt i=1; iend()->getDouble(id); if (valn > val) { val = valn; } } elems[0] = new TableExprNodeSetElem(TableExprNode(val), elem.isRightClosed()); } else if (elem.end() == 0) { // No end value, so only the lowest start value is relevant. Double val = elem.start()->getDouble(id); for (uInt i=1; istart()->getDouble(id); if (valn < val) { val = valn; } } elems[0] = new TableExprNodeSetElem(elem.isLeftClosed(), TableExprNode(val)); } else { // The intervals contain both a start and an end value. // Make the block large enough for all possible intervals. elems.resize (itsElems.size()); uInt nelem = 0; // Get all start values and sort them (indirectly) in ascending order. Block vals(itsElems.size()); for (uInt i=0; istart()->getDouble(id); } Vector index; GenSortIndirect::sort (index, vals, vals.nelements()); // Get the start and end value of first interval in sorted list. Double stval = vals[index[0]]; Double endval = itsElems[index[0]]->end()->getDouble(id); // Loop through the next intervals and combine if possible. for (uInt i=1; iend()->getDouble(id); // Combine intervals if they overlap. // They do if the next interval starts before end of this one // or if starting at the end and one side of the interval is closed. if (st2 < endval || (st2 == endval && (elem.isLeftClosed() || elem.isRightClosed()))) { // Overlap; update end if higher. if (end2 > endval) { endval = end2; } } else { // No overlap, so create the interval found and start a new one. elems[nelem++] = new TableExprNodeSetElem(elem.isLeftClosed(), stval, endval, elem.isRightClosed()); stval = st2; endval = end2; } } // Create the last interval and resize the array to #intervals found. elems[nelem++] = new TableExprNodeSetElem(elem.isLeftClosed(), stval, endval, elem.isRightClosed()); elems.resize (nelem); // Store the values in a start and an end array. itsStart.resize (nelem); itsEnd.resize (nelem); for (uInt i=0; istart()->getDouble(id); itsEnd[i] = elems[i]->end()->getDouble(id); } setFindFunc (elem.isLeftClosed(), elem.isRightClosed()); itsAllIntervals = True; } // Delete all existing intervals and replace by new ones. deleteElems(); itsElems = elems; } void TableExprNodeSet::combineDateIntervals() { DebugAssert (itsElems.size() > 0, AipsError); // Make an id (with an arbitrary row number) for the gets. // Note that this function uses the automatic Double<->MVTime conversions. TableExprId id(0); std::vector elems(1); TableExprNodeSetElem& elem = *(itsElems[0]); if (elem.start() == 0) { // No start value, so only the highest end value is relevant. // Make a single interval with the used open/closed-ness. Double val = elem.end()->getDate(id); for (uInt i=1; iend()->getDate(id); if (valn > val) { val = valn; } } elems[0] = new TableExprNodeSetElem (TableExprNode(new TableExprNodeConstDate(val)), elem.isRightClosed()); } else if (elem.end() == 0) { // No end value, so only the lowest start value is relevant. Double val = elem.start()->getDate(id); for (uInt i=1; istart()->getDate(id); if (valn < val) { val = valn; } } elems[0] = new TableExprNodeSetElem (elem.isLeftClosed(), TableExprNode(new TableExprNodeConstDate(val))); } else { // The intervals contain both a start and an end value. // Make the block large enough for all possible intervals. elems.resize (itsElems.size()); uInt nelem = 0; // Get all start values and sort them (indirectly) in ascending order. Block vals(itsElems.size()); for (uInt i=0; istart()->getDate(id); } Vector index; GenSortIndirect::sort (index, vals, vals.nelements()); // Get the start and end value of first interval in sorted list. Double stval = vals[index[0]]; Double endval = itsElems[index[0]]->end()->getDate(id); // Loop through the next intervals and combine if possible. for (uInt i=1; iend()->getDate(id); // Combine intervals if they overlap. // They do if the next interval starts before end of this one // or if starting at the end and one side of the interval is closed. if (st2 < endval || (st2 == endval && (elem.isLeftClosed() || elem.isRightClosed()))) { // Overlap; update end if higher. if (end2 > endval) { endval = end2; } } else { // No overlap, so create the interval found and start a new one. elems[nelem++] = new TableExprNodeSetElem (elem.isLeftClosed(), new TableExprNodeConstDate(stval), new TableExprNodeConstDate(endval), elem.isRightClosed()); stval = st2; endval = end2; } } // Create the last interval and resize the array to #intervals found. elems[nelem++] = new TableExprNodeSetElem (elem.isLeftClosed(), new TableExprNodeConstDate(stval), new TableExprNodeConstDate(endval), elem.isRightClosed()); elems.resize (nelem); // Store the values in a start and an end array. itsStart.resize (nelem); itsEnd.resize (nelem); for (uInt i=0; istart()->getDate(id); itsEnd[i] = elems[i]->end()->getDate(id); } setFindFunc (elem.isLeftClosed(), elem.isRightClosed()); itsAllIntervals = True; } // Delete all existing intervals and replace by new ones. deleteElems(); itsElems = elems; } void TableExprNodeSet::setFindFunc (Bool isLeftClosed, Bool isRightClosed) { if (isLeftClosed) { if (isRightClosed) { itsFindFunc = &TableExprNodeSet::findClosedClosed; } else { itsFindFunc = &TableExprNodeSet::findClosedOpen; } } else { if (isRightClosed) { itsFindFunc = &TableExprNodeSet::findOpenClosed; } else { itsFindFunc = &TableExprNodeSet::findOpenOpen; } } } void TableExprNodeSet::add (const TableExprNodeSetElem& elem, Bool adaptType) { uInt n = itsElems.size(); itsElems.resize (n+1); itsElems[n] = new TableExprNodeSetElem (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); } checkTablePtr (itsElems[n]); fillExprType (itsElems[n]); } void TableExprNodeSet::adaptSetUnits (const Unit& unit) { if (! unit.empty()) { for (uInt i=0; iadaptSetUnits (unit); } setUnit (unit); } } void TableExprNodeSet::checkEqualDataTypes() const { if (itsCheckTypes) { for (uInt 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 (uInt j=0; jshow (os, indent+2); } } void TableExprNodeSet::getAggrNodes (vector& aggr) { for (uInt j=0; jgetAggrNodes (aggr); } } void TableExprNodeSet::getColumnNodes (vector& cols) { for (uInt j=0; jgetColumnNodes (cols); } } Bool TableExprNodeSet::hasArrays() const { //# Check if a value is an array? uInt n = itsElems.size(); for (uInt i=0; ivalueType() == VTArray) { return True; } if (elem.end() != 0 && elem.end()->valueType() == VTArray) { return True; } if (elem.increment() != 0 && elem.increment()->valueType() == VTArray) { return True; } } return False; } TableExprNodeRep* TableExprNodeSet::setOrArray() const { // A set where elements have different unit types cannot be turned // into an array. if (! unit().empty()) { Quantity q(1., unit()); uInt n = nelements(); for (uInt i=0; iunit().empty()) { if (! q.isConform (itsElems[i]->unit())) { return new TableExprNodeSet (*this); } } } // No different units, so adapt elements to first one. for (uInt i=0; iadaptSetUnits (unit()); } } // When discrete, all start values should be filled in. if (itsDiscrete) { uInt n = nelements(); for (uInt i=0; istart() == 0) { throw (TableInvExpr ("no start value in discrete interval")); } } } // When the set is bounded, it can be converted to an array. if (itsBounded) { // When it is const, that can be done immediately. if (isConstant()) { return toConstArray(); } } TableExprNodeSet* set = new TableExprNodeSet (*this); if (itsBounded) { // Set the type to VTArray and the getArray // functions convert the set to an array for each row. set->setValueType (VTArray); if (itsSingle && !hasArrays()) { set->ndim_p = 1; set->shape_p = IPosition (1, nelements()); } } return set; } TableExprNodeRep* TableExprNodeSet::toConstArray() const { // Construct the correct const array object. TableExprNodeRep* tsnptr=0; switch (dataType()) { case NTBool: tsnptr = new TableExprNodeArrayConstBool (toArray(0)); break; case NTInt: tsnptr = new TableExprNodeArrayConstInt (toArray(0)); break; case NTDouble: tsnptr = new TableExprNodeArrayConstDouble (toArray(0)); break; case NTComplex: tsnptr = new TableExprNodeArrayConstDComplex (toArray(0)); break; case NTString: tsnptr = new TableExprNodeArrayConstString (toArray(0)); break; case NTDate: tsnptr = new TableExprNodeArrayConstDate (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::findOpenOpen (Double value) { uInt n = itsElems.size(); if (value >= itsEnd[n-1]) { return False; } for (uInt i=0; i itsEnd[n-1]) { return False; } for (uInt i=0; i= itsEnd[n-1]) { return False; } for (uInt i=0; i itsEnd[n-1]) { return False; } for (uInt i=0; imatchBool (&result, &value, 1, id); } return result; } Bool TableExprNodeSet::hasInt (const TableExprId& id, Int64 value) { if (itsAllIntervals) { return (this->*itsFindFunc) (value); } Bool result = False; uInt n = itsElems.size(); for (uInt i=0; imatchInt (&result, &value, 1, id); } return result; } Bool TableExprNodeSet::hasDouble (const TableExprId& id, Double value) { if (itsAllIntervals) { return (this->*itsFindFunc) (value); } Bool result = False; uInt n = itsElems.size(); for (uInt i=0; imatchDouble (&result, &value, 1, id); } return result; } Bool TableExprNodeSet::hasDComplex (const TableExprId& id, const DComplex& value) { Bool result = False; uInt n = itsElems.size(); for (uInt i=0; imatchDComplex (&result, &value, 1, id); } return result; } Bool TableExprNodeSet::hasString (const TableExprId& id, const String& value) { Bool result = False; uInt n = itsElems.size(); for (uInt i=0; imatchString (&result, &value, 1, id); } return result; } Bool TableExprNodeSet::hasDate (const TableExprId& id, const MVTime& value) { if (itsAllIntervals) { return (this->*itsFindFunc) (value); } Bool result = False; uInt n = itsElems.size(); for (uInt i=0; imatchDate (&result, &value, 1, id); } return result; } MArray TableExprNodeSet::hasArrayBool (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); uInt nval = value.nelements(); uInt n = itsElems.size(); for (uInt i=0; imatchBool (out, in, nval, id); } value.array().freeStorage (in, deleteIn); result.putStorage (out, deleteOut); return MArray (result, value.mask()); } MArray TableExprNodeSet::hasArrayInt (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); uInt nval = value.nelements(); uInt n = itsElems.size(); for (uInt i=0; imatchInt (out, in, nval, id); } value.array().freeStorage (in, deleteIn); result.putStorage (out, deleteOut); return MArray (result, value.mask()); } MArray TableExprNodeSet::hasArrayDouble (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); uInt nval = value.nelements(); uInt n = itsElems.size(); for (uInt i=0; imatchDouble (out, in, nval, id); } value.array().freeStorage (in, deleteIn); result.putStorage (out, deleteOut); return MArray (result, value.mask()); } MArray TableExprNodeSet::hasArrayDComplex (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); uInt nval = value.nelements(); uInt n = itsElems.size(); for (uInt i=0; imatchDComplex (out, in, nval, id); } value.array().freeStorage (in, deleteIn); result.putStorage (out, deleteOut); return MArray (result, value.mask()); } MArray TableExprNodeSet::hasArrayString (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); uInt nval = value.nelements(); uInt n = itsElems.size(); for (uInt i=0; imatchString (out, in, nval, id); } value.array().freeStorage (in, deleteIn); result.putStorage (out, deleteOut); return MArray (result, value.mask()); } MArray TableExprNodeSet::hasArrayDate (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); uInt nval = value.nelements(); uInt n = itsElems.size(); for (uInt 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-2.4.1/tables/TaQL/ExprNodeSet.h000066400000000000000000000501071321422335000177200ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: ExprNodeSet.h 21262 2012-09-07 12:38:36Z gervandiepen $ #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; template class Vector; // // 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 is used to assemble the table expression nodes // representing an element in a set. A set element can be of 3 types: //
          //
        1. A single discrete value, which can be of any type. // 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 interval (see below). //
        2. A discrete interval consisting of start, end and increment. // Each of those has to be an int scalar. Increment defaults to 1. // It can be used for 2 purposes: //
          - A slice in an array indexing operation. In that case start // defaults to the beginning of the dimension and end defaults to the end. //
          - A discrete interval in a set. Start has to be given. // When end is not given, the result is an unbounded discrete interval. // For a discrete interval, the type of start and end can also be // a datetime scalar. //
        3. A continuous interval, which can only be used in a set. // It consists of a start and/or an end scalar value of type int, double, // datetime, or string. The interval can be open or closed on one or // both sides. //
        // Note the difference between a discrete and a continuous interval. // E.g. the discrete interval 2,6 consists of the five values 2,3,4,5,6. // The continuous interval 2,6 consists of all values between them. //
        Further note that a bounded discrete interval is automatically // converted to a vector, which makes it possible to apply array // functions to it. //
        class TableExprNodeSetElem : public TableExprNodeRep { public: // Create the object for a single expression node. explicit TableExprNodeSetElem (const TableExprNode& node); // Create the object for a discrete interval. // 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); // Copy constructor (copy semantics). TableExprNodeSetElem (const TableExprNodeSetElem&); ~TableExprNodeSetElem(); // Show the node. void show (ostream& os, uInt indent) const; // Get the nodes representing an aggregate function. virtual void getAggrNodes (vector& aggr); // Get the nodes representing a table column. virtual void getColumnNodes (vector& cols); // Is it a discrete set element. Bool isDiscrete() const; // Is a single value given? Bool isSingle() const; // Is the interval left or right closed? // Bool isLeftClosed() const; Bool isRightClosed() const; // // Get the start, end or increment expression. // Note that the pointer returned can be zero indicating that that // value was not given. // TableExprNodeRep* start() const; TableExprNodeRep* end() const; TableExprNodeRep* increment() const; // // 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; void fillVector (Vector& vec, Int64& cnt, const TableExprId& id) const; void fillVector (Vector& vec, Int64& cnt, const TableExprId& id) const; void fillVector (Vector& vec, Int64& cnt, const TableExprId& id) const; void fillVector (Vector& vec, Int64& cnt, const TableExprId& id) const; 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. // void matchBool (Bool* match, const Bool* value, uInt nval, const TableExprId& id) const; void matchInt (Bool* match, const Int64* value, uInt nval, const TableExprId& id) const; void matchDouble (Bool* match, const Double* value, uInt nval, const TableExprId& id) const; void matchDComplex (Bool* match, const DComplex* value, uInt nval, const TableExprId& id) const; void matchString (Bool* match, const String* value, uInt nval, const TableExprId& id) const; void matchDate (Bool* match, const MVTime* value, uInt 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. TableExprNodeSetElem* evaluate (const TableExprId& id) const; // Get the table of a node and check if the children use the same table. void checkTable(); // Let a set node convert itself to the given unit. virtual void adaptSetUnits (const Unit&); private: // A copy of a TableExprNodeSetElem cannot be made. TableExprNodeSetElem& operator= (const TableExprNodeSetElem&); // 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. TableExprNodeSetElem (const TableExprNodeSetElem& that, TableExprNodeRep* start, TableExprNodeRep* end, TableExprNodeRep* incr); // Setup the object for a continuous interval. void setup (Bool isLeftClosed, const TableExprNode* start, const TableExprNode* end, Bool isRightClosed); TableExprNodeRep* itsStart; TableExprNodeRep* itsEnd; TableExprNodeRep* itsIncr; Bool itsEndExcl; Bool itsLeftClosed; Bool itsRightClosed; Bool itsDiscrete; Bool itsSingle; }; inline Bool TableExprNodeSetElem::isDiscrete() const { return itsDiscrete; } inline Bool TableExprNodeSetElem::isSingle() const { return itsSingle; } inline Bool TableExprNodeSetElem::isLeftClosed() const { return itsLeftClosed; } inline Bool TableExprNodeSetElem::isRightClosed() const { return itsRightClosed; } inline TableExprNodeRep* TableExprNodeSetElem::start() const { return itsStart; } inline TableExprNodeRep* TableExprNodeSetElem::end() const { return itsEnd; } inline TableExprNodeRep* TableExprNodeSetElem::increment() const { return itsIncr; } // // 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.nelements() 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(); // 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. void add (const TableExprNodeSetElem&, Bool adaptType=False); // Show the node. void show (ostream& os, uInt indent) const; // Get the nodes representing an aggregate function. virtual void getAggrNodes (vector& aggr); // Get the nodes representing a table column. virtual void getColumnNodes (vector& cols); // 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 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. uInt nelements() const; // Get the i-th element. const TableExprNodeSetElem& operator[] (uInt 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. TableExprNodeRep* setOrArray() const; template MArray toArray (const TableExprId& id) const; // Get an array value for this bounded set in the given row. // 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); // // Does a value occur in the set? // virtual Bool hasBool (const TableExprId& id, Bool value); virtual Bool hasInt (const TableExprId& id, Int64 value); virtual Bool hasDouble (const TableExprId& id, Double value); virtual Bool hasDComplex (const TableExprId& id, const DComplex& value); virtual Bool hasString (const TableExprId& id, const String& value); virtual Bool hasDate (const TableExprId& id, const MVTime& value); virtual MArray hasArrayBool (const TableExprId& id, const MArray& value); virtual MArray hasArrayInt (const TableExprId& id, const MArray& value); virtual MArray hasArrayDouble (const TableExprId& id, const MArray& value); virtual MArray hasArrayDComplex (const TableExprId& id, const MArray& value); virtual MArray hasArrayString (const TableExprId& id, const MArray& value); virtual MArray hasArrayDate (const TableExprId& id, const MArray& value); // // Let a set node convert itself to the given unit. virtual void adaptSetUnits (const Unit&); private: // A copy of a TableExprNodeSet cannot be made. TableExprNodeSet& operator= (const TableExprNodeSet&); // Delete all set elements in itsElems. void deleteElems(); // Convert the const set to an array. TableExprNodeRep* toConstArray() const; // Get the array in a templated way. // void getArray (MArray& marr, TableExprNodeRep* node, const TableExprId& id) const { marr.reference (node->getArrayBool (id)); } void getArray (MArray& marr, TableExprNodeRep* node, const TableExprId& id) const { marr.reference (node->getArrayInt (id)); } void getArray (MArray& marr, TableExprNodeRep* node, const TableExprId& id) const { marr.reference (node->getArrayDouble (id)); } void getArray (MArray& marr, TableExprNodeRep* node, const TableExprId& id) const { marr.reference (node->getArrayDComplex (id)); } void getArray (MArray& marr, TableExprNodeRep* node, const TableExprId& id) const { marr.reference (node->getArrayString (id)); } void getArray (MArray& marr, TableExprNodeRep* node, const TableExprId& id) const { marr.reference (node->getArrayDate (id)); } // // Sort and combine intervals. // void combineIntIntervals(); void combineDoubleIntervals(); void combineDateIntervals(); // // Define the functions to find a double, which depend on open/closed-ness. // In this way a test on open/closed is done only once. // typedef Bool (TableExprNodeSet::* FindFuncPtr) (Double value); Bool findOpenOpen (Double value); Bool findOpenClosed (Double value); Bool findClosedOpen (Double value); Bool findClosedClosed (Double value); void setFindFunc (Bool isLeftClosed, Bool isRightClosed); // 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 Bool itsAllIntervals; //# True = all elements are const intervals (sorted) Block itsStart; //# Start values of const intervals Block itsEnd; //# End values of const intervals FindFuncPtr itsFindFunc; //# Function to find a matching const interval }; inline Bool TableExprNodeSet::isSingle() const { return itsSingle; } inline Bool TableExprNodeSet::isDiscrete() const { return itsDiscrete; } inline Bool TableExprNodeSet::isBounded() const { return itsBounded; } inline uInt TableExprNodeSet::nelements() const { return itsElems.size(); } inline const TableExprNodeSetElem& TableExprNodeSet::operator[] (uInt index) const { return *(itsElems[index]); } template MArray TableExprNodeSet::toArray (const TableExprId& id) const { // TODO: align possible units DebugAssert (itsBounded, AipsError); Int64 n = nelements(); if (hasArrays()) { // 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)); result.resize (shp, True); if (! mask.empty()) mask.resize (shp, True); ArrayIterator iter(result, shp.size()-1); IPosition s(shp); IPosition e(shp); s[naxes] = 0; e[naxes] = 0; for (Int64 i=1; i 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()) { mask.resize (shp); mask = False; } mask(s,e) = marr.mask(); } else if (! mask.empty()) { mask(s,e) = False; } } return MArray(result, mask); } else { Int64 n = nelements(); 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-2.4.1/tables/TaQL/ExprRange.cc000066400000000000000000000127611321422335000175550ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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. uInt nrres=0; Vector stres (sval_p.nelements() + that.sval_p.nelements()); Vector endres(sval_p.nelements() + that.sval_p.nelements()); uInt i,j; //# Loop through all intervals of this. for (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. uInt nrres=0; Vector stres (sval_p.nelements() + that.sval_p.nelements()); Vector endres(sval_p.nelements() + that.sval_p.nelements()); uInt i; uInt j=0; //# Loop through all intervals of this. //# Store in the result, while inserting the that intervals, //# in order of start-value. for (i=0; i stmp(nrres); Vector etmp(nrres); j=0; stmp(0) = stres(0); // first interval etmp(0) = endres(0); for (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-2.4.1/tables/TaQL/ExprRange.h000066400000000000000000000110041321422335000174040ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/tables/TaQL/ExprUDFNode.cc000066400000000000000000000075141321422335000177450ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN TableExprUDFNode::TableExprUDFNode (UDFBase* udf, const Table& tab, const TableExprNodeSet& source) : TableExprNodeMulti (udf->dataType(), VTScalar, OtFunc, source), itsUDF (udf) { // Set the table. This is needed for ExprNode::checkReplaceTable to work. table_p = tab; // 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 (is also fine if undefined). setUnit (Unit(udf->getUnit())); } TableExprUDFNode::~TableExprUDFNode() { delete itsUDF; } void TableExprUDFNode::getAggrNodes (vector& aggr) { uInt naggr = aggr.size(); itsUDF->getAggrNodes (aggr); if (itsUDF->isAggregate()) { // If the UDF itself is an aggregate function, its operands should not // contain aggregate functions. if (naggr != aggr.size()) { throw TableInvExpr ("The argument of an aggregate function cannot use " "an aggregate function"); } aggr.push_back (this); } } void TableExprUDFNode::getColumnNodes (vector& cols) { itsUDF->getColumnNodes (cols); cols.push_back (this); } void TableExprUDFNode::disableApplySelection() { itsUDF->disableApplySelection(); } void TableExprUDFNode::applySelection (const Vector& rownrs) { itsUDF->applySelection (rownrs); } CountedPtr TableExprUDFNode::makeGroupAggrFunc() { return new TableExprGroupNull(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-2.4.1/tables/TaQL/ExprUDFNode.h000066400000000000000000000106251321422335000176040ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (UDFBase* udf, const Table&, const TableExprNodeSet& source); // Destructor virtual ~TableExprUDFNode(); // Is the UDF an aggregate function? Bool isAggregate() const { return itsUDF->isAggregate(); } // Get the nodes representing an aggregate function. virtual void getAggrNodes (vector& aggr); // Get the nodes representing a table column. virtual void getColumnNodes (vector& cols); // Do not apply the selection. virtual void disableApplySelection(); // If needed, let the UDF re-create column objects for a selection of rows. // It calls the function recreateColumnObjects. virtual void applySelection (const Vector& rownrs); // UDFs do not need a TableExprGroupFuncBase, // so TableExprGroupNull is returned. CountedPtr makeGroupAggrFunc(); // Functions to get the desired result of a 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 TaqlRegex getRegex (const TableExprId& id); virtual MVTime getDate (const TableExprId& id); // private: UDFBase* itsUDF; }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/tables/TaQL/ExprUDFNodeArray.cc000066400000000000000000000076331321422335000207460ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: ExprUDFNodeArray.cc 21262 2012-09-07 12:38:36Z gervandiepen $ //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN TableExprUDFNodeArray::TableExprUDFNodeArray (UDFBase* udf, const Table& tab, const TableExprNodeSet&) : TableExprNodeArray (udf->dataType(), OtFunc), itsUDF (udf) { // Set the table. This is needed for ExprNode::checkReplaceTable to work. table_p = tab; // 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 (is also fine if undefined). setUnit (Unit(udf->getUnit())); } TableExprUDFNodeArray::~TableExprUDFNodeArray() { delete itsUDF; } void TableExprUDFNodeArray::getAggrNodes (vector& aggr) { uInt naggr = aggr.size(); itsUDF->getAggrNodes (aggr); if (itsUDF->isAggregate()) { // If the UDF itself is an aggregate function, its operands should not // contain aggregate functions. if (naggr != aggr.size()) { throw TableInvExpr ("The argument of an aggregate function cannot use " "an aggregate function"); } aggr.push_back (this); } } void TableExprUDFNodeArray::getColumnNodes (vector& cols) { itsUDF->getColumnNodes (cols); cols.push_back (this); } void TableExprUDFNodeArray::disableApplySelection() { itsUDF->disableApplySelection(); } void TableExprUDFNodeArray::applySelection (const Vector& rownrs) { itsUDF->applySelection (rownrs); } CountedPtr TableExprUDFNodeArray::makeGroupAggrFunc() { return new TableExprGroupNull(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-2.4.1/tables/TaQL/ExprUDFNodeArray.h000066400000000000000000000105401321422335000205770ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: ExprUDFNodeArray.h 21262 2012-09-07 12:38:36Z gervandiepen $ #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 (UDFBase* udf, const Table&, const TableExprNodeSet& source); // Destructor virtual ~TableExprUDFNodeArray(); // Get the nodes representing an aggregate function. virtual void getAggrNodes (vector& aggr); // Get the nodes representing a table column. virtual void getColumnNodes (vector& cols); // Do not apply the selection. virtual void disableApplySelection(); // If needed, let the UDF re-create column objects for a selection of rows. // It calls the function recreateColumnObjects. virtual void applySelection (const Vector& rownrs); // UDFs do not need a TableExprGroupFuncBase, so null is returned. CountedPtr makeGroupAggrFunc(); // 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); // private: UDFBase* itsUDF; }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/tables/TaQL/ExprUnitNode.cc000066400000000000000000000133231321422335000202410ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: ExprUnitNode.cc 21262 2012-09-07 12:38:36Z gervandiepen $ #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN TableExprNodeUnit::TableExprNodeUnit (TableExprNodeRep& 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.link(); factor_p = set (*this, child, unit); } TableExprNodeUnit::~TableExprNodeUnit() {} Double TableExprNodeUnit::set (TableExprNodeRep& parent, const TableExprNodeRep& 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; } TableExprNodeRep* TableExprNodeUnit::useUnit (TableExprNodeRep* const 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. TableExprNodeBinary* tsnptr; if (node->valueType() == VTScalar) { tsnptr = new TableExprNodeUnit (*node, unit); } else { tsnptr = new TableExprNodeArrayUnit (*node, unit); } if (tsnptr->getUnitFactor() == 1.) { // Units are the same, so no conversion needed. delete tsnptr; return node; } return tsnptr; } void TableExprNodeUnit::adaptUnit (TableExprNodeRep*& node, const Unit& unit) { // See if a conversion is needed. // If so, adapt the reference counts and replace it. TableExprNodeRep* nnode = useUnit (node, unit); if (nnode != node) { unlink (node); node = nnode->link(); } } Unit TableExprNodeUnit::adaptUnits (TableExprNodeRep*& node1, TableExprNodeRep*& node2, TableExprNodeRep*& 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 (TableExprNodeRep& 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.link(); 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-2.4.1/tables/TaQL/ExprUnitNode.h000066400000000000000000000103171321422335000201030ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: ExprUnitNode.h 21262 2012-09-07 12:38:36Z gervandiepen $ #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: // Constrcut from the given child node and unit. TableExprNodeUnit (TableExprNodeRep& 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 TableExprNodeRep& child, const Unit& unit); // Create a new node if unit conversion is needed. // Otherwise return the current node. static TableExprNodeRep* useUnit (TableExprNodeRep* const 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 (TableExprNodeRep*& node, const Unit& unit); // Find the unit to be used and adapt the nodes to it. static Unit adaptUnits (TableExprNodeRep*& node1, TableExprNodeRep*& node2, TableExprNodeRep*& 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 (TableExprNodeRep& 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-2.4.1/tables/TaQL/MArray.h000066400000000000000000000166321321422335000167200ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: MArray.h 21399 2013-11-12 07:55:35Z gervandiepen $ #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 " + String::toString(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-2.4.1/tables/TaQL/MArrayBase.cc000066400000000000000000000100441321422335000176400ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: MArrayBase.cc 21262 2012-09-07 12:38:36Z gervandiepen $ //# 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-2.4.1/tables/TaQL/MArrayBase.h000066400000000000000000000135161321422335000175110ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: MArrayBase.h 21399 2013-11-12 07:55:35Z gervandiepen $ #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-2.4.1/tables/TaQL/MArrayLogical.h000066400000000000000000000527301321422335000202120ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: MArrayLogical.h 21262 2012-09-07 12:38:36Z gervandiepen $ #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()) { return MArray(slidingNTrue (a.array(), halfBoxSize, fillEdge)); } 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()) { return MArray(slidingNFalse (a.array(), halfBoxSize, fillEdge)); } 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()) { return MArray(boxedNTrue (a.array(), boxSize)); } 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()) { return MArray(boxedNFalse (a.array(), boxSize)); } 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-2.4.1/tables/TaQL/MArrayMath.h000066400000000000000000001342401321422335000175260ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: MArrayMath.h 21262 2012-09-07 12:38:36Z gervandiepen $ #ifndef CASA_MARRAYMATH_H #define CASA_MARRAYMATH_H //# Includes #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: virtual ~MVarianceFunc() {} T operator() (const MArray& arr) const { return variance(arr); } }; template class MStddevFunc : public MArrayFunctorBase { public: virtual ~MStddevFunc() {} T operator() (const MArray& arr) const { return stddev(arr); } }; 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 Double& 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) { Int64 nv = a.nvalid(); if (nv < 2) return T(); if (! a.hasMask()) return variance(a.array(), mean); 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 - 1)); } template T variance(const MArray& a) { return variance(a, mean(a)); } template T stddev(const MArray& a) { return sqrt(variance(a)); } template T stddev(const MArray& a, T mean) { return sqrt(variance(a, mean)); } 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 median 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) { if (a.isNull()) { return MArray(); } else if (! a.hasMask()) { return MArray(partialVariances (a.array(), collapseAxes)); } return partialArrayMath (a, collapseAxes, MVarianceFunc()); } template MArray partialStddevs (const MArray& a, const IPosition& collapseAxes) { if (a.isNull()) { return MArray(); } else if (! a.hasMask()) { return MArray(partialStddevs (a.array(), collapseAxes)); } return partialArrayMath (a, collapseAxes, MStddevFunc()); } 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, Bool fillEdge=True) { if (a.isNull()) { return MArray(); } else if (! a.hasMask()) { return MArray(slidingArrayMath (a.array(), halfBoxSize, VarianceFunc(), fillEdge)); } return slidingArrayMath (a, halfBoxSize, MVarianceFunc(), fillEdge); } template MArray slidingStddevs (const MArray& a, const IPosition& halfBoxSize, Bool fillEdge=True) { if (a.isNull()) { return MArray(); } else if (! a.hasMask()) { return MArray(slidingArrayMath (a.array(), halfBoxSize, StddevFunc(), fillEdge)); } return slidingArrayMath (a, halfBoxSize, MStddevFunc(), 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) { if (a.isNull()) { return MArray(); } else if (! a.hasMask()) { return MArray(boxedArrayMath (a.array(), boxSize, VarianceFunc())); } return boxedArrayMath (a, boxSize, MVarianceFunc()); } template MArray boxedStddevs (const MArray& a, const IPosition& boxSize) { if (a.isNull()) { return MArray(); } else if (! a.hasMask()) { return MArray(boxedArrayMath (a.array(), boxSize, StddevFunc())); } return boxedArrayMath (a, boxSize, MStddevFunc()); } 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-2.4.1/tables/TaQL/MArrayMathBase.h000066400000000000000000000234141321422335000203210ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: MArrayMathBase.h 21262 2012-09-07 12:38:36Z gervandiepen $ #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-2.4.1/tables/TaQL/MArrayUtil.h000066400000000000000000000066021321422335000175520ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: MArrayUtil.h 21262 2012-09-07 12:38:36Z gervandiepen $ #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)))); } // } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/tables/TaQL/RecordExpr.cc000066400000000000000000000070331321422335000177330ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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 (AipsError ("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 (AipsError ("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 (AipsError ("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-2.4.1/tables/TaQL/RecordExpr.h000066400000000000000000000060721321422335000175770ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/tables/TaQL/RecordGram.cc000066400000000000000000000227011321422335000177020ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ // 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 // routines used by bison actions #include #include #include #include //# 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. #define register #include "RecordGram.ycc" // bison output #include "RecordGram.lcc" // flex output // 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; 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) { delete ptr; removeToken (ptr); } void RecordGram::deleteToken (RecordGramVal* ptr) { delete ptr; removeToken (ptr); } void RecordGram::deleteToken (TableExprNodeSet* ptr) { delete ptr; removeToken (ptr); } void RecordGram::deleteToken (TableExprNodeSetElem* ptr) { delete ptr; removeToken (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) + "'")); } TableExprNode RecordGram::parse (const RecordInterface& record, const String& expression) { ScopedMutexLock lock(theirMutex); theirRecPtr = &record; theirTabPtr = 0; return doParse (expression); } TableExprNode RecordGram::parse (const Table& table, const String& expression) { ScopedMutexLock 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 AipsError& x) { message = x.getMesg(); 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 theirTabPtr->keyCol (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 TableParseSelect::makeFuncNode (0, name, arguments, ignoreFuncs, Table(), theirTaQLStyle); } return TableParseSelect::makeFuncNode (0, name, arguments, Vector(), *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))); if (negate) { lnode = (lnode != rnode); } else { lnode = (lnode == rnode); } return lnode; } } //# NAMESPACE CASACORE - END casacore-2.4.1/tables/TaQL/RecordGram.h000066400000000000000000000201531321422335000175430ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef TABLES_RECORDGRAM_H #define TABLES_RECORDGRAM_H //# Includes #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class TableExprNode; class TableExprNodeSet; class TableExprNodeSetElem; class RecordInterface; 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); // Create a TableExprNode from a literal. static TableExprNode handleLiteral (RecordGramVal*); // Find the field name and create a TableExprNode from it. static TableExprNode handleField (const String& name); // Handle a function. 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. 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. static void deleteToken (TableExprNode* ptr); static void deleteToken (RecordGramVal* ptr); static void deleteToken (TableExprNodeSet* ptr); static void deleteToken (TableExprNodeSetElem* ptr); // Delete all tokens not deleted yet. static void deleteTokenStorage(); private: // 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 Mutex theirMutex; }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/tables/TaQL/RecordGram.ll000066400000000000000000000262471321422335000177350ustar00rootroot00000000000000/* 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: aips2-request@nrao.edu. Postal address: AIPS++ Project Office National Radio Astronomy Observatory 520 Edgemont Road Charlottesville, VA 22903-2475 USA $Id$ */ /* 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}+ 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 ("-"{INT}?"-")|("-"?[A-Za-z]+"-"?) DATEH {INT}{MONTH}{INT} DATES {INT}"/"{INT}?"/"{INT} DATE {DATEH}|{DATES} DTIMEHM {INT}[hH]({INT}?([mM]({FLINT})?)?)? DTIMEC {INT}":"({INT}?(":"({FLINT})?)?)? DTIME {DTIMEHM}|{DTIMEC} DATETIME {DATE}([-/]{DTIME})? POSDM {INT}[dD]{INT}[mM]{FLINT}? POSD {INT}"."{INT}?"."{FLINT}? TIME {DTIMEHM}|{POSDM}|{POSD} /* positions with colons cannot be allowed, because they interfere with the interval syntax (and a starting slash is rather ambiguous). TIME {DTIMEHM}|{TIMESL}|{TIMEU}|{POSDM}|{POSD} */ 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 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->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-2.4.1/tables/TaQL/RecordGram.yy000066400000000000000000000514441321422335000177640ustar00rootroot00000000000000/* 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: aips2-request@nrao.edu. Postal address: AIPS++ Project Office National Radio Astronomy Observatory 520 Edgemont Road Charlottesville, VA 22903-2475 USA $Id$ */ /* 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 LIKE %token LPAREN %token RPAREN %token COMMA %token LBRACKET %token RBRACKET %token LBRACE %token RBRACE %token COLON %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 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 (TableParseSelect::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 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-2.4.1/tables/TaQL/TaQLNode.cc000066400000000000000000000172271321422335000172730ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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; 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'; } ScopedMutexLock 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); 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); } 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-2.4.1/tables/TaQL/TaQLNode.h000066400000000000000000000167221321422335000171340ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef TABLES_TAQLNODE_H #define TABLES_TAQLNODE_H //# Includes #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() : itsRep(0) {} // Construct for given letter. It takes over the pointer. TaQLNode (TaQLNodeRep* rep) { itsRep = TaQLNodeRep::link (rep); } // Copy constructor (reference semantics). TaQLNode (const TaQLNode& that) { itsRep = TaQLNodeRep::link (that.itsRep); } // Assignment (reference semantics). TaQLNode& operator= (const TaQLNode& that) { if (this != &that) { TaQLNodeRep::unlink (itsRep); itsRep = TaQLNodeRep::link (that.itsRep); } return *this; } // Get the TaQL style. const TaQLStyle& style() const { return itsRep->style(); } // Destructor deletes the letter if no more references. ~TaQLNode() { TaQLNodeRep::unlink (itsRep); } // Parse a TaQL command and return the result. // An exception is thrown in case of parse errors. static TaQLNode parse (const String& command); // Does the envelope contain a letter? Bool isValid() const { return itsRep; } // Return the type of letter. char nodeType() const { return itsRep->nodeType(); } // Get read access to the letter. const TaQLNodeRep* getRep() const { return itsRep; } // 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 tree. // void save (AipsIO& aio) const; static TaQLNode restore (AipsIO& aio); // protected: TaQLNodeRep* itsRep; private: 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 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-2.4.1/tables/TaQL/TaQLNodeDer.cc000066400000000000000000001452411321422335000177240ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# Includes #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN TaQLConstNodeRep::TaQLConstNodeRep (Bool value) : TaQLNodeRep (TaQLNode_Const), itsType (CTBool), itsIsTableName (False), itsBValue (value) {} TaQLConstNodeRep::TaQLConstNodeRep (Int64 value, Bool isTableName) : TaQLNodeRep (TaQLNode_Const), itsType (CTInt), itsIsTableName (isTableName), 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() {} 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 << '$'; } 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 << 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; break; case CTReal: aio << itsRValue; break; case CTComplex: aio << itsCValue; break; case CTString: aio << itsSValue; break; case CTTime: aio << (double)itsTValue; break; } } TaQLConstNodeRep* 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; return new TaQLConstNodeRep (value, isTableName); } 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) {} TaQLRegexNodeRep::~TaQLRegexNodeRep() {} 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; } TaQLRegexNodeRep* 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) {} TaQLUnaryNodeRep::~TaQLUnaryNodeRep() {} 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); } TaQLUnaryNodeRep* 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() {} 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); } TaQLBinaryNodeRep* 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) {} TaQLMultiNodeRep::~TaQLMultiNodeRep() {} 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; TaQLMultiNodeRep* node = new TaQLMultiNodeRep(prefix, postfix, isSetOrArray); node->setSeparator (sep); node->setSeparator (incr, sep2); for (uInt i=0; iadd (TaQLNode::restoreNode (aio)); } return node; } 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) {} TaQLFuncNodeRep::~TaQLFuncNodeRep() {} 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); } TaQLFuncNodeRep* TaQLFuncNodeRep::restore (AipsIO& aio) { String name; aio >> name; return new TaQLFuncNodeRep (name, TaQLNode::restoreMultiNode (aio)); } TaQLRangeNodeRep::TaQLRangeNodeRep (Bool leftClosed, TaQLNode start, const TaQLNode& end, Bool rightClosed) : TaQLNodeRep (TaQLNode_Range), itsLeftClosed (leftClosed), itsStart (start), itsEnd (end), itsRightClosed(rightClosed) {} TaQLRangeNodeRep::TaQLRangeNodeRep (Bool leftClosed, const TaQLNode& start) : TaQLNodeRep (TaQLNode_Range), itsLeftClosed (leftClosed), itsStart (start), itsEnd (), itsRightClosed(False) {} TaQLRangeNodeRep::TaQLRangeNodeRep (const TaQLNode& end, Bool rightClosed) : TaQLNodeRep (TaQLNode_Range), itsLeftClosed (False), itsStart (), itsEnd (end), itsRightClosed(rightClosed) {} TaQLRangeNodeRep::~TaQLRangeNodeRep() {} TaQLNodeResult TaQLRangeNodeRep::visit (TaQLNodeVisitor& visitor) const { return visitor.visitRangeNode (*this); } void TaQLRangeNodeRep::show (std::ostream& os) const { 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; itsStart.saveNode (aio); itsEnd.saveNode (aio); } TaQLRangeNodeRep* TaQLRangeNodeRep::restore (AipsIO& aio) { Bool leftClosed, rightClosed; aio >> leftClosed >> rightClosed; TaQLNode start = TaQLNode::restoreNode (aio); TaQLNode end = TaQLNode::restoreNode (aio); return new TaQLRangeNodeRep (leftClosed, start, end, rightClosed); } TaQLIndexNodeRep::TaQLIndexNodeRep (const TaQLNode& start, const TaQLNode& end, const TaQLNode& incr) : TaQLNodeRep (TaQLNode_Index), itsStart (start), itsEnd (end), itsIncr (incr) {} TaQLIndexNodeRep::~TaQLIndexNodeRep() {} 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); } TaQLIndexNodeRep* 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) {} TaQLJoinNodeRep::~TaQLJoinNodeRep() {} 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 CONDITION "; itsCondition.show (os); } void TaQLJoinNodeRep::save (AipsIO& aio) const { itsTables.saveNode (aio); itsCondition.saveNode (aio); } TaQLJoinNodeRep* 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) {} TaQLKeyColNodeRep::~TaQLKeyColNodeRep() {} 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; } TaQLKeyColNodeRep* 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) {} TaQLTableNodeRep::~TaQLTableNodeRep() {} 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); } TaQLTableNodeRep* 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)) {} TaQLColNodeRep::~TaQLColNodeRep() {} 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); } TaQLColNodeRep* TaQLColNodeRep::restore (AipsIO& aio) { String name, nameMask, dtype; aio >> name >> nameMask >> dtype; TaQLColNodeRep* node = new TaQLColNodeRep (TaQLNode::restoreNode(aio), name, nameMask, dtype); return node; } TaQLColumnsNodeRep::TaQLColumnsNodeRep (Bool distinct, const TaQLMultiNode& nodes) : TaQLNodeRep (TaQLNode_Columns), itsDistinct (distinct), itsNodes (nodes) {} TaQLColumnsNodeRep::~TaQLColumnsNodeRep() {} 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); } TaQLColumnsNodeRep* 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) {} TaQLGroupNodeRep::~TaQLGroupNodeRep() {} 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); } TaQLGroupNodeRep* 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) {} TaQLSortKeyNodeRep::~TaQLSortKeyNodeRep() {} 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); } TaQLSortKeyNodeRep* 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) {} TaQLSortNodeRep::~TaQLSortNodeRep() {} 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); } TaQLSortNodeRep* 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) {} TaQLLimitOffNodeRep::~TaQLLimitOffNodeRep() {} 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); } TaQLLimitOffNodeRep* 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) {} TaQLGivingNodeRep::~TaQLGivingNodeRep() {} 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 << 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); } } TaQLGivingNodeRep* 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) {} TaQLUpdExprNodeRep::~TaQLUpdExprNodeRep() {} 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); } TaQLUpdExprNodeRep* 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) {} TaQLQueryNodeRep::~TaQLQueryNodeRep() {} 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 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), itsWhere(where), itsGroupby(groupby), itsHaving(having), itsSort(sort), itsLimitOff(limitoff), itsGiving(giving), itsDMInfo(dminfo) {} TaQLSelectNodeRep::TaQLSelectNodeRep (const TaQLNode& columns, const TaQLMultiNode& tables, const TaQLNode& join, 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), itsTables(tables), itsJoin(join), itsWhere(where), itsGroupby(groupby), itsHaving(having), itsSort(sort), itsLimitOff(limitoff), itsGiving(giving), itsDMInfo(dminfo) {} TaQLSelectNodeRep::~TaQLSelectNodeRep() {} TaQLNodeResult TaQLSelectNodeRep::visit (TaQLNodeVisitor& visitor) const { return visitor.visitSelectNode (*this); } void TaQLSelectNodeRep::showDerived (std::ostream& os) const { os << "SELECT"; itsColumns.show (os); if (itsTables.isValid()) { os << " FROM "; itsTables.show (os); } itsJoin.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); itsTables.saveNode (aio); itsJoin.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); } TaQLSelectNodeRep* TaQLSelectNodeRep::restore (AipsIO& aio) { TaQLNode columns = TaQLNode::restoreNode (aio); TaQLMultiNode tables = TaQLNode::restoreMultiNode (aio); TaQLNode join = 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); TaQLSelectNodeRep* node = new TaQLSelectNodeRep (columns, tables, join, where, groupby, having, sort, limitoff, giving, dminfo); node->restoreSuper (aio); return node; } TaQLCountNodeRep::TaQLCountNodeRep (const TaQLNode& columns, const TaQLMultiNode& tables, const TaQLNode& where) : TaQLQueryNodeRep (TaQLNode_Count), itsColumns(columns), itsTables(tables), itsWhere(where) {} TaQLCountNodeRep::~TaQLCountNodeRep() {} TaQLNodeResult TaQLCountNodeRep::visit (TaQLNodeVisitor& visitor) const { return visitor.visitCountNode (*this); } void TaQLCountNodeRep::showDerived (std::ostream& os) const { os << "COUNT "; itsColumns.show (os); os << " FROM "; itsTables.show (os); if (itsWhere.isValid()) { os << " WHERE "; itsWhere.show (os); } } void TaQLCountNodeRep::save (AipsIO& aio) const { itsColumns.saveNode (aio); itsTables.saveNode (aio); itsWhere.saveNode (aio); saveSuper (aio); } TaQLCountNodeRep* TaQLCountNodeRep::restore (AipsIO& aio) { TaQLNode columns = TaQLNode::restoreNode (aio); TaQLMultiNode tables = TaQLNode::restoreMultiNode (aio); TaQLNode where = TaQLNode::restoreNode (aio); TaQLCountNodeRep* node = new TaQLCountNodeRep (columns, tables, where); node->restoreSuper (aio); return node; } TaQLUpdateNodeRep::TaQLUpdateNodeRep (const TaQLMultiNode& tables, const TaQLMultiNode& update, const TaQLMultiNode& from, const TaQLNode& where, const TaQLNode& sort, const TaQLNode& limitoff) : TaQLNodeRep (TaQLNode_Update), itsTables (tables), itsUpdate (update), itsFrom (from), itsWhere (where), itsSort (sort), itsLimitOff (limitoff) {} TaQLUpdateNodeRep::~TaQLUpdateNodeRep() {} TaQLNodeResult TaQLUpdateNodeRep::visit (TaQLNodeVisitor& visitor) const { return visitor.visitUpdateNode (*this); } void TaQLUpdateNodeRep::show (std::ostream& os) const { 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 { itsTables.saveNode (aio); itsUpdate.saveNode (aio); itsFrom.saveNode (aio); itsWhere.saveNode (aio); itsSort.saveNode (aio); itsLimitOff.saveNode (aio); } TaQLUpdateNodeRep* TaQLUpdateNodeRep::restore (AipsIO& 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 (tables, update, from, where, sort, limitoff); } TaQLInsertNodeRep::TaQLInsertNodeRep (const TaQLMultiNode& tables, const TaQLMultiNode& columns, const TaQLNode& values, const TaQLNode& limit) : TaQLNodeRep (TaQLNode_Insert), itsTables (tables), itsColumns (columns), itsValues (values), itsLimit (limit) {} TaQLInsertNodeRep::TaQLInsertNodeRep (const TaQLMultiNode& tables, const TaQLMultiNode& insert) : TaQLNodeRep (TaQLNode_Insert), 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; } TaQLInsertNodeRep::~TaQLInsertNodeRep() {} TaQLNodeResult TaQLInsertNodeRep::visit (TaQLNodeVisitor& visitor) const { return visitor.visitInsertNode (*this); } void TaQLInsertNodeRep::show (std::ostream& os) const { 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 { itsTables.saveNode (aio); itsColumns.saveNode (aio); itsValues.saveNode (aio); itsLimit.saveNode (aio); } TaQLInsertNodeRep* TaQLInsertNodeRep::restore (AipsIO& aio) { TaQLMultiNode tables = TaQLNode::restoreMultiNode (aio); TaQLMultiNode columns = TaQLNode::restoreMultiNode (aio); TaQLNode values = TaQLNode::restoreNode (aio); TaQLNode limit = TaQLNode::restoreNode (aio); return new TaQLInsertNodeRep (tables, columns, values, limit); } TaQLDeleteNodeRep::TaQLDeleteNodeRep (const TaQLMultiNode& tables, const TaQLNode& where, const TaQLNode& sort, const TaQLNode& limitoff) : TaQLNodeRep (TaQLNode_Delete), itsTables (tables), itsWhere (where), itsSort (sort), itsLimitOff (limitoff) {} TaQLDeleteNodeRep::~TaQLDeleteNodeRep() {} TaQLNodeResult TaQLDeleteNodeRep::visit (TaQLNodeVisitor& visitor) const { return visitor.visitDeleteNode (*this); } void TaQLDeleteNodeRep::show (std::ostream& os) const { 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 { itsTables.saveNode (aio); itsWhere.saveNode (aio); itsSort.saveNode (aio); itsLimitOff.saveNode (aio); } TaQLDeleteNodeRep* TaQLDeleteNodeRep::restore (AipsIO& aio) { TaQLMultiNode tables = TaQLNode::restoreMultiNode (aio); TaQLNode where = TaQLNode::restoreNode (aio); TaQLNode sort = TaQLNode::restoreNode (aio); TaQLNode limitoff = TaQLNode::restoreNode (aio); return new TaQLDeleteNodeRep (tables, where, sort, limitoff); } TaQLCalcNodeRep::TaQLCalcNodeRep (const TaQLMultiNode& tables, const TaQLNode& expr, const TaQLNode& where, const TaQLNode& sort, const TaQLNode& limitoff) : TaQLNodeRep (TaQLNode_Calc), itsTables (tables), itsExpr (expr), itsWhere (where), itsSort (sort), itsLimitOff (limitoff) {} TaQLCalcNodeRep::~TaQLCalcNodeRep() {} TaQLNodeResult TaQLCalcNodeRep::visit (TaQLNodeVisitor& visitor) const { return visitor.visitCalcNode (*this); } void TaQLCalcNodeRep::show (std::ostream& os) const { 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 { itsTables.saveNode (aio); itsExpr.saveNode (aio); itsWhere.saveNode (aio); itsSort.saveNode (aio); itsLimitOff.saveNode (aio); } TaQLCalcNodeRep* TaQLCalcNodeRep::restore (AipsIO& 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 (tables, expr, where, sort, limitoff); } TaQLCreTabNodeRep::TaQLCreTabNodeRep (const TaQLNode& giving, const TaQLMultiNode& cols, const TaQLNode& limit, const TaQLMultiNode& dminfo) : TaQLQueryNodeRep (TaQLNode_CreTab), itsGiving (giving), itsColumns (cols), itsLimit (limit), itsDMInfo (dminfo) {} TaQLCreTabNodeRep::~TaQLCreTabNodeRep() {} TaQLNodeResult TaQLCreTabNodeRep::visit (TaQLNodeVisitor& visitor) const { return visitor.visitCreTabNode (*this); } void TaQLCreTabNodeRep::showDerived (std::ostream& os) const { os << "CREATE TABLE "; itsGiving.show (os); 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 { itsGiving.saveNode (aio); itsColumns.saveNode (aio); itsLimit.saveNode (aio); itsDMInfo.saveNode (aio); saveSuper (aio); } TaQLCreTabNodeRep* TaQLCreTabNodeRep::restore (AipsIO& aio) { TaQLNode giving = TaQLNode::restoreNode (aio); TaQLMultiNode columns = TaQLNode::restoreMultiNode (aio); TaQLNode limit = TaQLNode::restoreNode (aio); TaQLMultiNode dminfo = TaQLNode::restoreMultiNode (aio); TaQLCreTabNodeRep* node = new TaQLCreTabNodeRep (giving, columns, limit, dminfo); node->restoreSuper (aio); return node; } TaQLColSpecNodeRep::TaQLColSpecNodeRep (const String& name, const String& dtype, const TaQLMultiNode& spec) : TaQLNodeRep (TaQLNode_ColSpec), itsName (name), itsDtype (checkDataType(dtype)), itsSpec (spec) {} TaQLColSpecNodeRep::~TaQLColSpecNodeRep() {} TaQLNodeResult TaQLColSpecNodeRep::visit (TaQLNodeVisitor& visitor) const { return visitor.visitColSpecNode (*this); } void TaQLColSpecNodeRep::show (std::ostream& os) const { os << itsName; if (! itsDtype.empty()) { os << ' ' << itsDtype; } if (itsSpec.isValid()) { os << ' '; itsSpec.show (os); } } void TaQLColSpecNodeRep::save (AipsIO& aio) const { aio << itsName << itsDtype; itsSpec.saveNode (aio); } TaQLColSpecNodeRep* TaQLColSpecNodeRep::restore (AipsIO& aio) { String name, dtype; aio >> name >> dtype; TaQLMultiNode spec = TaQLNode::restoreMultiNode (aio); return new TaQLColSpecNodeRep (name, 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)) {} TaQLRecFldNodeRep::~TaQLRecFldNodeRep() {} 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 && ((const TaQLMultiNodeRep*)(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); } TaQLRecFldNodeRep* 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) {} TaQLUnitNodeRep::~TaQLUnitNodeRep() {} 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); } TaQLUnitNodeRep* TaQLUnitNodeRep::restore (AipsIO& aio) { String unit; aio >> unit; TaQLNode node = TaQLNode::restoreNode (aio); return new TaQLUnitNodeRep (unit, node); } TaQLAltTabNodeRep::TaQLAltTabNodeRep (const TaQLNode& table, const TaQLMultiNode& from, const TaQLMultiNode& commands) : TaQLQueryNodeRep (TaQLNode_AltTab), itsTable (table), itsFrom (from), itsCommands (commands) {} TaQLAltTabNodeRep::~TaQLAltTabNodeRep() {} TaQLNodeResult TaQLAltTabNodeRep::visit (TaQLNodeVisitor& visitor) const { return visitor.visitAltTabNode (*this); } void TaQLAltTabNodeRep::showDerived (std::ostream& os) const { os << "ALTER TABLE "; itsTable.show (os); if (itsFrom.isValid()) { os << " FROM "; itsFrom.show (os); } os << ' '; itsCommands.show (os); } void TaQLAltTabNodeRep::save (AipsIO& aio) const { itsTable.saveNode (aio); itsFrom.saveNode (aio); itsCommands.saveNode (aio); } TaQLAltTabNodeRep* TaQLAltTabNodeRep::restore (AipsIO& aio) { TaQLNode table = TaQLNode::restoreNode (aio); TaQLMultiNode from = TaQLNode::restoreMultiNode (aio); TaQLMultiNode commands = TaQLNode::restoreMultiNode (aio); return new TaQLAltTabNodeRep (table, from, commands); } TaQLAddColNodeRep::TaQLAddColNodeRep (const TaQLMultiNode& cols, const TaQLMultiNode& dminfo) : TaQLNodeRep (TaQLNode_AddCol), itsColumns (cols), itsDMInfo (dminfo) {} TaQLAddColNodeRep::~TaQLAddColNodeRep() {} 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); } TaQLAddColNodeRep* 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) {} TaQLRenDropNodeRep::~TaQLRenDropNodeRep() {} 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); } TaQLRenDropNodeRep* 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) {} TaQLSetKeyNodeRep::~TaQLSetKeyNodeRep() {} 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); } TaQLSetKeyNodeRep* TaQLSetKeyNodeRep::restore (AipsIO& aio) { TaQLMultiNode keyvals = TaQLNode::restoreMultiNode (aio); return new TaQLSetKeyNodeRep (keyvals); } TaQLAddRowNodeRep::TaQLAddRowNodeRep (const TaQLNode& nrow) : TaQLNodeRep (TaQLNode_AddRow), itsNRow(nrow) {} TaQLAddRowNodeRep::~TaQLAddRowNodeRep() {} 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); } TaQLAddRowNodeRep* 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) {} TaQLConcTabNodeRep::~TaQLConcTabNodeRep() {} 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 " << itsTableName; } os << ']'; } void TaQLConcTabNodeRep::save (AipsIO& aio) const { aio << itsTableName; itsTables.saveNode (aio); itsSubTables.saveNode (aio); } TaQLConcTabNodeRep* 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) {} TaQLShowNodeRep::~TaQLShowNodeRep() {} TaQLNodeResult TaQLShowNodeRep::visit (TaQLNodeVisitor& visitor) const { return visitor.visitShowNode (*this); } void TaQLShowNodeRep::show (std::ostream& os) const { if (itsNames.isValid()) { itsNames.show (os); } } void TaQLShowNodeRep::save (AipsIO& aio) const { itsNames.saveNode (aio); } TaQLShowNodeRep* TaQLShowNodeRep::restore (AipsIO& aio) { TaQLMultiNode names = TaQLNode::restoreMultiNode (aio); return new TaQLShowNodeRep (names); } } //# NAMESPACE CASACORE - END casacore-2.4.1/tables/TaQL/TaQLNodeDer.h000066400000000000000000001210351321422335000175610ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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, Bool isTableName=False); 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); virtual ~TaQLConstNodeRep(); void setIsTableName() { itsIsTableName = True; } const String& getString() const; const String& getUnit() const { return itsUnit; } virtual TaQLNodeResult visit (TaQLNodeVisitor&) const; virtual void show (std::ostream& os) const; virtual void save (AipsIO& aio) const; static TaQLConstNodeRep* 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 ~TaQLRegexNodeRep(); virtual TaQLNodeResult visit (TaQLNodeVisitor&) const; virtual void show (std::ostream& os) const; virtual void save (AipsIO& aio) const; static TaQLRegexNodeRep* 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 ~TaQLUnaryNodeRep(); virtual TaQLNodeResult visit (TaQLNodeVisitor&) const; virtual void show (std::ostream& os) const; virtual void save (AipsIO& aio) const; static TaQLUnaryNodeRep* 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 ~TaQLBinaryNodeRep(); virtual TaQLNodeResult visit (TaQLNodeVisitor&) const; virtual void show (std::ostream& os) const; virtual void save (AipsIO& aio) const; static TaQLBinaryNodeRep* 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); virtual ~TaQLMultiNodeRep(); 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; virtual void show (std::ostream& os) const; virtual void save (AipsIO& aio) const; static TaQLMultiNodeRep* 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 ~TaQLFuncNodeRep(); virtual TaQLNodeResult visit (TaQLNodeVisitor&) const; virtual void show (std::ostream& os) const; virtual void save (AipsIO& aio) const; static TaQLFuncNodeRep* 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, TaQLNode start, const TaQLNode& end, Bool rightClosed); TaQLRangeNodeRep (Bool leftClosed, const TaQLNode& start); TaQLRangeNodeRep (const TaQLNode& end, Bool rightClosed); virtual ~TaQLRangeNodeRep(); virtual TaQLNodeResult visit (TaQLNodeVisitor&) const; virtual void show (std::ostream& os) const; virtual void save (AipsIO& aio) const; static TaQLRangeNodeRep* restore (AipsIO& aio); Bool itsLeftClosed; TaQLNode itsStart; TaQLNode itsEnd; Bool itsRightClosed; }; // // 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 ~TaQLIndexNodeRep(); virtual TaQLNodeResult visit (TaQLNodeVisitor&) const; virtual void show (std::ostream& os) const; virtual void save (AipsIO& aio) const; static TaQLIndexNodeRep* 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 ~TaQLJoinNodeRep(); virtual TaQLNodeResult visit (TaQLNodeVisitor&) const; virtual void show (std::ostream& os) const; virtual void save (AipsIO& aio) const; static TaQLJoinNodeRep* 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 ~TaQLKeyColNodeRep(); virtual TaQLNodeResult visit (TaQLNodeVisitor&) const; virtual void show (std::ostream& os) const; virtual void save (AipsIO& aio) const; static TaQLKeyColNodeRep* 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 ~TaQLTableNodeRep(); virtual TaQLNodeResult visit (TaQLNodeVisitor&) const; virtual void show (std::ostream& os) const; virtual void save (AipsIO& aio) const; static TaQLTableNodeRep* 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 ~TaQLColNodeRep(); virtual TaQLNodeResult visit (TaQLNodeVisitor&) const; virtual void show (std::ostream& os) const; virtual void save (AipsIO& aio) const; static TaQLColNodeRep* 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 ~TaQLColumnsNodeRep(); virtual TaQLNodeResult visit (TaQLNodeVisitor&) const; virtual void show (std::ostream& os) const; virtual void save (AipsIO& aio) const; static TaQLColumnsNodeRep* 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 ~TaQLGroupNodeRep(); virtual TaQLNodeResult visit (TaQLNodeVisitor&) const; virtual void show (std::ostream& os) const; virtual void save (AipsIO& aio) const; static TaQLGroupNodeRep* 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 ~TaQLSortKeyNodeRep(); virtual TaQLNodeResult visit (TaQLNodeVisitor&) const; virtual void show (std::ostream& os) const; virtual void save (AipsIO& aio) const; static TaQLSortKeyNodeRep* 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 ~TaQLSortNodeRep(); virtual TaQLNodeResult visit (TaQLNodeVisitor&) const; virtual void show (std::ostream& os) const; virtual void save (AipsIO& aio) const; static TaQLSortNodeRep* 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 ~TaQLLimitOffNodeRep(); virtual TaQLNodeResult visit (TaQLNodeVisitor&) const; virtual void show (std::ostream& os) const; virtual void save (AipsIO& aio) const; static TaQLLimitOffNodeRep* 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 ~TaQLGivingNodeRep(); virtual TaQLNodeResult visit (TaQLNodeVisitor&) const; virtual void show (std::ostream& os) const; virtual void save (AipsIO& aio) const; static TaQLGivingNodeRep* 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 ~TaQLUpdExprNodeRep(); virtual TaQLNodeResult visit (TaQLNodeVisitor&) const; virtual void show (std::ostream& os) const; virtual void save (AipsIO& aio) const; static TaQLUpdExprNodeRep* 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); virtual ~TaQLQueryNodeRep(); 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; protected: virtual void saveSuper (AipsIO& aio) const; virtual 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 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& tables, const TaQLNode& join, const TaQLNode& where, const TaQLNode& groupby, const TaQLNode& having, const TaQLNode& sort, const TaQLNode& limitoff, const TaQLNode& giving, const TaQLMultiNode& dminfo); virtual ~TaQLSelectNodeRep(); virtual TaQLNodeResult visit (TaQLNodeVisitor&) const; virtual void showDerived (std::ostream& os) const; virtual void save (AipsIO& aio) const; static TaQLSelectNodeRep* restore (AipsIO& aio); TaQLNode itsColumns; TaQLMultiNode itsTables; TaQLNode itsJoin; 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 TaQLNode& columns, const TaQLMultiNode& tables, const TaQLNode& where); virtual ~TaQLCountNodeRep(); virtual TaQLNodeResult visit (TaQLNodeVisitor&) const; virtual void showDerived (std::ostream& os) const; virtual void save (AipsIO& aio) const; static TaQLCountNodeRep* restore (AipsIO& aio); 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& tables, const TaQLMultiNode& update, const TaQLMultiNode& from, const TaQLNode& where, const TaQLNode& sort, const TaQLNode& limitoff); virtual ~TaQLUpdateNodeRep(); virtual TaQLNodeResult visit (TaQLNodeVisitor&) const; virtual void show (std::ostream& os) const; virtual void save (AipsIO& aio) const; static TaQLUpdateNodeRep* restore (AipsIO& aio); 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& tables, const TaQLMultiNode& columns, const TaQLNode& values, const TaQLNode& limit); TaQLInsertNodeRep (const TaQLMultiNode& tables, const TaQLMultiNode& insert); virtual ~TaQLInsertNodeRep(); virtual TaQLNodeResult visit (TaQLNodeVisitor&) const; virtual void show (std::ostream& os) const; virtual void save (AipsIO& aio) const; static TaQLInsertNodeRep* restore (AipsIO& aio); 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& tables, const TaQLNode& where, const TaQLNode& sort, const TaQLNode& limitoff); virtual ~TaQLDeleteNodeRep(); virtual TaQLNodeResult visit (TaQLNodeVisitor&) const; virtual void show (std::ostream& os) const; virtual void save (AipsIO& aio) const; static TaQLDeleteNodeRep* restore (AipsIO& aio); 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& tables, const TaQLNode& expr, const TaQLNode& where, const TaQLNode& sort, const TaQLNode& limitoff); virtual ~TaQLCalcNodeRep(); virtual TaQLNodeResult visit (TaQLNodeVisitor&) const; virtual void show (std::ostream& os) const; virtual void save (AipsIO& aio) const; static TaQLCalcNodeRep* restore (AipsIO& aio); 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 TaQLNode& giving, const TaQLMultiNode& cols, const TaQLNode& limit, const TaQLMultiNode& dminfo); virtual ~TaQLCreTabNodeRep(); virtual TaQLNodeResult visit (TaQLNodeVisitor&) const; virtual void showDerived (std::ostream& os) const; virtual void save (AipsIO& aio) const; static TaQLCreTabNodeRep* restore (AipsIO& aio); TaQLNode itsGiving; 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& dtype, const TaQLMultiNode& spec); virtual ~TaQLColSpecNodeRep(); virtual TaQLNodeResult visit (TaQLNodeVisitor&) const; virtual void show (std::ostream& os) const; virtual void save (AipsIO& aio) const; static TaQLColSpecNodeRep* restore (AipsIO& aio); String itsName; 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 ~TaQLRecFldNodeRep(); virtual TaQLNodeResult visit (TaQLNodeVisitor&) const; virtual void show (std::ostream& os) const; virtual void save (AipsIO& aio) const; static TaQLRecFldNodeRep* 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 ~TaQLUnitNodeRep(); virtual TaQLNodeResult visit (TaQLNodeVisitor&) const; virtual void show (std::ostream& os) const; virtual void save (AipsIO& aio) const; static TaQLUnitNodeRep* 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 TaQLNode& table, const TaQLMultiNode& from, const TaQLMultiNode& commands); virtual ~TaQLAltTabNodeRep(); virtual TaQLNodeResult visit (TaQLNodeVisitor&) const; virtual void showDerived (std::ostream& os) const; virtual void save (AipsIO& aio) const; static TaQLAltTabNodeRep* restore (AipsIO& aio); 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 ~TaQLAddColNodeRep(); virtual TaQLNodeResult visit (TaQLNodeVisitor&) const; virtual void show (std::ostream& os) const; virtual void save (AipsIO& aio) const; static TaQLAddColNodeRep* 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 ~TaQLRenDropNodeRep(); virtual TaQLNodeResult visit (TaQLNodeVisitor&) const; virtual void show (std::ostream& os) const; virtual void save (AipsIO& aio) const; static TaQLRenDropNodeRep* 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 ~TaQLSetKeyNodeRep(); virtual TaQLNodeResult visit (TaQLNodeVisitor&) const; virtual void show (std::ostream& os) const; virtual void save (AipsIO& aio) const; static TaQLSetKeyNodeRep* 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 ~TaQLAddRowNodeRep(); virtual TaQLNodeResult visit (TaQLNodeVisitor&) const; virtual void show (std::ostream& os) const; virtual void save (AipsIO& aio) const; static TaQLAddRowNodeRep* 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 ~TaQLConcTabNodeRep(); virtual TaQLNodeResult visit (TaQLNodeVisitor&) const; virtual void showDerived (std::ostream& os) const; virtual void save (AipsIO& aio) const; static TaQLConcTabNodeRep* 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 ~TaQLShowNodeRep(); virtual TaQLNodeResult visit (TaQLNodeVisitor&) const; virtual void show (std::ostream& os) const; virtual void save (AipsIO& aio) const; static TaQLShowNodeRep* restore (AipsIO& aio); TaQLMultiNode itsNames; }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/tables/TaQL/TaQLNodeHandler.cc000066400000000000000000001133001321422335000205560ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN TaQLNodeHRValue::~TaQLNodeHRValue() { // Note that itsExprSet and itsElem are counted-referenced in itsExpr, // so that takes care of deletion. delete itsNames; } TaQLNodeHandler::~TaQLNodeHandler() { clearStack(); } TaQLNodeResult TaQLNodeHandler::handleTree (const TaQLNode& node, const std::vector& tempTables) { clearStack(); itsTempTables = tempTables; return node.visit (*this); } TableParseSelect* TaQLNodeHandler::pushStack (TableParseSelect::CommandType type) { TableParseSelect* sel = new TableParseSelect(type); itsStack.push_back (sel); return sel; } TableParseSelect* 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); expr.useUnit ("d"); 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)))); } 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 (TableParseSelect::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); 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) { TaQLNodeResult result = visitNode (node.itsArgs); TaQLNodeHRValue* hrval = new TaQLNodeHRValue(); TaQLNodeResult res(hrval); hrval->setExpr (topStack()->handleFunc (node.itsName, getHR(result).getExprSet(), node.style())); return res; } 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 (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) { TaQLConstNodeRep* tabnm = (TaQLConstNodeRep*)(node.itsTable.getRep()); if (tabnm->itsType == TaQLConstNodeRep::CTInt) { hrval->setInt (tabnm->itsIValue); } else { hrval->setString (tabnm->getString()); } } 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&) { throw TableInvExpr ("join is not supported yet"); return TaQLNodeResult(); } TaQLNodeResult TaQLNodeHandler::visitGroupNode (const TaQLGroupNodeRep& node) { const TaQLMultiNodeRep* keys = node.itsNodes.getMultiRep(); const std::vector& 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 TableError("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] = TableParseSort (res.getExpr()); } else { Sort::Order sortOrder = Sort::Ascending; if (keyNode->itsType == TaQLSortKeyNodeRep::Descending) { sortOrder = Sort::Descending; } outkeys[i] = TableParseSort (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 (new TableParseUpdate(node.itsName, node.itsNameMask, getHR(ires1).getExprSet(), getHR(ires2).getExprSet(), expr, node.itsIndices1.style())); } else { topStack()->addUpdate (new TableParseUpdate(node.itsName, node.itsNameMask, getHR(ires1).getExprSet(), expr, node.itsIndices1.style())); } } else { topStack()->addUpdate (new TableParseUpdate(node.itsName, node.itsNameMask, expr)); } return TaQLNodeResult(); } TaQLNodeResult TaQLNodeHandler::visitSelectNode (const TaQLSelectNodeRep& node) { // Add an entry to the stack. Bool outer = itsStack.empty(); TableParseSelect* curSel = pushStack (TableParseSelect::PSELECT); // First handle LIMIT/OFFSET, because limit is needed when creating // a temp table for a select without a FROM. 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()->makeTableNoFrom (itsStack); } 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); visitNode (node.itsJoin); 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()); hrval->setTable (curSel->getTable()); hrval->setNames (new Vector(curSel->getColumnNames())); 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) { TableParseSelect* curSel = pushStack (TableParseSelect::PUPDATE); 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()); hrval->setNames (new Vector(curSel->getColumnNames())); hrval->setString ("update"); popStack(); return res; } TaQLNodeResult TaQLNodeHandler::visitInsertNode (const TaQLInsertNodeRep& node) { TableParseSelect* curSel = pushStack (TableParseSelect::PINSERT); 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()); hrval->setNames (new Vector(curSel->getColumnNames())); hrval->setString ("insert"); popStack(); return res; } TaQLNodeResult TaQLNodeHandler::visitDeleteNode (const TaQLDeleteNodeRep& node) { TableParseSelect* curSel = pushStack (TableParseSelect::PDELETE); 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(); TableParseSelect* curSel = pushStack (TableParseSelect::PCOUNT); 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()); hrval->setNames (new Vector(curSel->getColumnNames())); hrval->setString ("count"); } else { AlwaysAssert (node.getFromExecute(), AipsError); hrval->setTable (curSel->doFromQuery(node.style().doTiming())); } popStack(); return res; } TaQLNodeResult TaQLNodeHandler::visitCalcNode (const TaQLCalcNodeRep& node) { TableParseSelect* curSel = pushStack (TableParseSelect::PCALC); 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) { TableParseSelect* curSel = pushStack (TableParseSelect::PCRETAB); visitNode (node.itsGiving); 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); TaQLNodeHRValue* hrval = new TaQLNodeHRValue(); TaQLNodeResult res(hrval); hrval->setTable (curSel->getTable()); hrval->setNames (new Vector(curSel->getColumnNames())); hrval->setString ("cretab"); popStack(); return res; } TaQLNodeResult TaQLNodeHandler::visitColSpecNode (const TaQLColSpecNodeRep& node) { Record spec = handleMultiRecFld (node.itsSpec); topStack()->handleColSpec (node.itsName, 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.getNodeRep()->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 TpInt: 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(); handleTables (node.itsFrom); 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) { 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::handleTables (const TaQLMultiNode& node) { if (! node.isValid()) { return; } const std::vector& nodes = node.getMultiRep()->itsNodes; for (uInt i=0; iaddTable (res.getInt(), res.getString(), res.getTable(), res.getAlias(), itsTempTables, itsStack); } } TaQLNodeResult TaQLNodeHandler::visitConcTabNode (const TaQLConcTabNodeRep& node) { const std::vector& nodes = node.itsTables.getMultiRep()->itsNodes; std::vector
      • tables; for (uInt i=0; imakeTable (res.getInt(), name, res.getTable(), res.getAlias(), itsTempTables, itsStack, False)); if (!tab.isNull()) { tables.push_back (tab); } else if (name.empty()) { throw AipsError ("No matching tables found for $" + String::toString(res.getInt())); } else { Vector nms = Directory::shellExpand(Vector(1, name)); if (nms.empty()) { throw AipsError ("No matching tables found for " + name); } for (uInt j=0; jmakeTable (res.getInt(), nms[j], res.getTable(), res.getAlias(), itsTempTables, itsStack)); } } } Block
        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) { Vector parts; if (node.itsNames.isValid()) { const std::vector& names = node.itsNames.getMultiRep()->itsNodes; parts.resize (names.size()); for (uInt i=0; isetExpr (TableExprNode(info)); hr->setString ("show"); return hr; } 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 = *(const TaQLMultiNodeRep*)(node.getRep()); const std::vector& anodes = avals.itsNodes; std::vector exprs; AlwaysAssert (anodes.size() > 0, AipsError); uInt nval = 0; for (uInt i=0; i& 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 (new TableParseUpdate("", "", expr)); } } } topStack()->setInsertExprs (exprs); } 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 TableParseSelect 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); // // Get the actual result object from the result. static const TaQLNodeHRValue& getHR (const TaQLNodeResult&); private: // Push a new TableParseSelect on the stack. TableParseSelect* pushStack (TableParseSelect::CommandType); // Get the top of the TableParseSelect stack. TableParseSelect* topStack() const; // Pop the top from the TableParseSelect 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 MultiNode containing table info. void handleTables (const TaQLMultiNode&); // 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 a column specification in a create table. void handleColSpecs (const TaQLMultiNode&); // Handle a Multi RecFld representing a Record. Record handleMultiRecFld (const TaQLNode& node); //# Use vector instead of stack because it has random access //# (which is used in TableParse.cc). 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) {} virtual ~TaQLNodeHRValue(); // 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 (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; TableExprNodeSet* itsSet; Vector* itsNames; }; //# This function can only be implemented after TaQLNodeHRBase is declared. inline const TaQLNodeHRValue& TaQLNodeHandler::getHR (const TaQLNodeResult& res) { return *(TaQLNodeHRValue*)(res.getRep()); } } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/tables/TaQL/TaQLNodeRep.cc000066400000000000000000000061471321422335000177410ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN TaQLNodeRep::TaQLNodeRep (int nodeType) : itsCount (0), 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 == "INT8") { 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; } } //# NAMESPACE CASACORE - END casacore-2.4.1/tables/TaQL/TaQLNodeRep.h000066400000000000000000000125211321422335000175740ustar00rootroot00000000000000//# TaQLNodeRep.h: Representation of 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef TABLES_TAQLNODEREP_H #define TABLES_TAQLNODEREP_H //# Includes #include #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) // // Constructor for derived classes specifying the type. explicit TaQLNodeRep (int nodeType); virtual ~TaQLNodeRep(); // Increment the reference count. static TaQLNodeRep* link (TaQLNodeRep* rep) { if (rep) ++rep->itsCount; return rep; } // Decrement the reference count. // Delete the letter if no more references. static void unlink (TaQLNodeRep* rep) { if (rep && --rep->itsCount == 0) delete rep; } // 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&); private: // Letter objects cannot be copied. // TaQLNodeRep (const TaQLNodeRep&); TaQLNodeRep& operator= (const TaQLNodeRep&); // int itsCount; char itsNodeType; TaQLStyle itsStyle; }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/tables/TaQL/TaQLNodeResult.cc000066400000000000000000000026311321422335000204630ustar00rootroot00000000000000//# TaQLNodeResult.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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include namespace casacore { //# NAMESPACE CASACORE - BEGIN TaQLNodeResultRep::~TaQLNodeResultRep() {} } //# NAMESPACE CASACORE - END casacore-2.4.1/tables/TaQL/TaQLNodeResult.h000066400000000000000000000116111321422335000203230ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef TABLES_TAQLNODERESULT_H #define TABLES_TAQLNODERESULT_H //# Includes #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 clears the reference count. // The count is updated by functions link and unlink. TaQLNodeResultRep() : itsCount(0) {} // Destructor. virtual ~TaQLNodeResultRep(); // Increment the reference count. static TaQLNodeResultRep* link (TaQLNodeResultRep* rep) { if (rep) ++rep->itsCount; return rep; } // Decrement the reference count. // Delete the letter if no more references. static void unlink (TaQLNodeResultRep* rep) { if (rep && --rep->itsCount == 0) delete rep; } private: // Letter objects cannot be copied. // TaQLNodeResultRep (const TaQLNodeResultRep&); TaQLNodeResultRep& operator= (const TaQLNodeResultRep&); // int itsCount; }; // // 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() : itsRep(0) {} // Take the given letter and increment its reference count. TaQLNodeResult (TaQLNodeResultRep* rep) { itsRep = TaQLNodeResultRep::link (rep); } // Copy constructor (reference semantics). TaQLNodeResult (const TaQLNodeResult& that) { itsRep = TaQLNodeResultRep::link (that.itsRep); } // Assignment (reference semantics). TaQLNodeResult& operator= (const TaQLNodeResult& that) { if (this != &that) { TaQLNodeResultRep::unlink (itsRep); itsRep = TaQLNodeResultRep::link (that.itsRep); } return *this; } // Destructor decrements the reference count. // The letter is deleted if no more references. ~TaQLNodeResult() { TaQLNodeResultRep::unlink (itsRep); } // Does the envelope hold a letter? Bool isValid() const { return itsRep; } private: TaQLNodeResultRep* itsRep; public: // Get the actual underlying object. const TaQLNodeResultRep* getRep() const { return itsRep; } }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/tables/TaQL/TaQLNodeVisitor.cc000066400000000000000000000026351321422335000206500ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include namespace casacore { //# NAMESPACE CASACORE - BEGIN TaQLNodeVisitor::~TaQLNodeVisitor() {} } //# NAMESPACE CASACORE - END casacore-2.4.1/tables/TaQL/TaQLNodeVisitor.h000066400000000000000000000134001321422335000205020ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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; // 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-2.4.1/tables/TaQL/TaQLResult.cc000066400000000000000000000036051321422335000176570ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# Includes #include #include #include namespace casacore { TaQLResult::TaQLResult (const Table& table) : itsTable (table) {} // Also keep the table, otherwise the Table object is deleted and // the node contains a dangling BaseTable pointer. TaQLResult::TaQLResult (const TableExprNode& node) : itsTable (node.table()), itsNode (node) {} Table TaQLResult::table() const { AlwaysAssert (isTable(), AipsError); return itsTable; } TableExprNode TaQLResult::node() const { AlwaysAssert (!isTable(), AipsError); return itsNode; } } //#NAMESPACE CASACORE - END casacore-2.4.1/tables/TaQL/TaQLResult.h000066400000000000000000000054461321422335000175260ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef TABLES_TAQLRESULT_H #define TABLES_TAQLRESULT_H //# Includes #include #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. 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 Table. // It throws an exception if it is not a Table. Table table() const; // Make it possible to convert automatically to a Table //# (for backwards compatibility). operator Table() const { return table(); } // 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-2.4.1/tables/TaQL/TaQLShow.cc000066400000000000000000001335531321422335000173270ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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[] = { "SELECT", " [[DISTINCT] expression_list]", " [INTO table [AS options]]", " [FROM table_list]", " [WHERE expression]", " [GROUPBY expression_list]", " [HAVING expression]", " [ORDERBY [DISTINCT] [ASC|DESC] sort_list]", " [LIMIT expression] [OFFSET expression]", " [GIVING table [AS options] | set]", " [DMINFO datamanagers]", "", "[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, R4, R8, C4, C8, S, EPOCH", " Use 'show datatypes' to get more information about the possible data types.", " 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 nothing 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.", "", "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 (like 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." }; const char* calcHelp[] = { "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[] = { "UPDATE table_list", " SET update_list", " [FROM table_list]", " [WHERE expression]", " [ORDERBY [DISTINCT] [ASC|DESC] sort_list]", " [LIMIT expression] [OFFSET expression]", "", "This command makes it possible to update values in selected rows and columns", "of the first table in the table list.", "" }; const char* insertHelp[] = { "INSERT INTO table_list SET column=expr, column=expr, ...", "INSERT INTO table_list [(column_list)] VALUES (expr_list)", "INSERT INTO table_list [(column_list)] SELECT_command", "'show command insert' not implemented yet" }; const char* deleteHelp[] = { "DELETE FROM table_list", " [WHERE ...] [ORDERBY ...] [LIMIT ...] [OFFSET ...]", "'show command' delete' not implemented yet" }; const char* createHelp[] = { "CREATE TABLE table [AS options]", " [column_specs]", " [LIMIT ...]", " [DMINFO datamanagers]", "'show command create' not implemented yet" }; const char* alterHelp[] = { "ALTER TABLE table", " [ADD COLUMN [column_specs] [DMINFO datamanagers]", " [RENAME COLUMN old TO new, old TO new, ...]", " [DROP COLUMN col, col, ...]", " [SET KEYWORD key=value, key=value, ...]", " [COPY KEYWORD key=other, key=other, ...]", " [RENAME KEYWORD old TO new, old TO new, ...]", " [DROP KEYWORD key, key, ...]", " [ADD ROW nrow]", "'show command alter' not implemented yet" }; const char* countHelp[] = { "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 (like 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 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?" }; 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 !~", "", "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] like [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", " 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* 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 left out and defaults to 0", " 'end' can be left out making it unbounded (till infinity)", " ':step' can be left out 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 two ways:", " Using curly braces (closed bound) and angle brackets (open bound)", " bounded: {1,2} <1,2> {1,2> <1,2}", " unbounded: {1,} <1,> {,2> <,2}", " Using a=:=b (closed bounds) and a<: 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 over pixel", " 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 (real) variance", " double STDDEV (real) standard deviation", " double AVDEV (real) average deviation", " double RMS (real) root-mean-squares", " 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", " 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" }; const char* aggrFuncHelp[] = { "Aggregate functions operating per group (using GROUPBY)", "", "The following functions results 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 (real) variance", " double GSTDDEV (real) standard deviation", " double GAVDEV (real) average deviation", " double GRMS (real) root-mean-squares", " double GMEDIAN (real) median (the middle element)", " double GFRACTILE (real, fraction) element at given fraction", "", "The following functions results in an array and operate element by element", " GANYS GALLS GNTRUES GNFALSES", " GSUMS GSUMSQRS GPRODUCTS GMINS GMAXS", " GMEANS GVARIANCES GSTDDEVS GAVDEVS GRMSS", "", "The following functions results in a scalar value", " 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* frequencyHelp[] = { "'show meastype frequency' not implemented yet" }; const char* dopplerHelp[] = { "'show meastype doppler' not implemented yet" }; const char* radVelHelp[] = { "'show meastype radialvelocity' not implemented yet" }; 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, style); } 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 == "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 AipsError (cmd + " is an unknown SHOW command"); } String TaQLShow::showTable (const Vector& parts, const TaQLStyle& style) { if (parts.size() < 2 || parts[1].empty()) { return getHelp (tableHelp); } Table table(Table::openTable(parts[1])); 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 AipsError (parts[i] + " is an unknown show table option; use: " "dm col sort key colkey recur"); } } std::ostringstream os; table.showStructure (os, showdm, showcol, showsub, sortcol, style.isCOrder()); table.showKeywords (os, showsub, tabkey, colkey); return os.str(); } 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 AipsError (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)); TableExprNode node = TableExprNode::newUDFNode (type+".help", operands, Table(), style); return node.getString(0); } 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 AipsError ("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 (frequencyHelp) + ///getHelp (dopplerHelp) + ///getHelp (radVelHelp) + "\nSee also 'show functions meas pos|epoch|dir\n"; /// "\nSee also 'show functions meas " /// "pos|epoch|dir|freq|doppler|radvel\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 == "freq" || type == "frequency") { return getHelp (frequencyHelp); } else if (type == "doppler") { return getHelp (dopplerHelp); } else if (type == "radvel" || type == "radialvelocity") { return getHelp (radVelHelp); } throw AipsError (type + " is an unknown type for command " "'show meastypes '"); } } //# NAMESPACE CASACORE - END casacore-2.4.1/tables/TaQL/TaQLShow.h000066400000000000000000000062551321422335000171670ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 // // // 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 TaQLShow { public: static String getInfo (const Vector& parts, const TaQLStyle& style); static String showTable (const Vector& parts, const TaQLStyle& style); 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-2.4.1/tables/TaQL/TaQLStyle.cc000066400000000000000000000065361321422335000175070ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/tables/TaQL/TaQLStyle.h000066400000000000000000000101251321422335000173360ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/tables/TaQL/TableExprData.cc000066400000000000000000000061551321422335000203420ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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-2.4.1/tables/TaQL/TableExprData.h000066400000000000000000000227731321422335000202100ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #ifndef TABLES_TABLEEXPRDATA_H #define TABLES_TABLEEXPRDATA_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class String; class IPosition; template class Array; 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-2.4.1/tables/TaQL/TableExprId.cc000066400000000000000000000026051321422335000200210ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Everything is inlined } //# NAMESPACE CASACORE - END casacore-2.4.1/tables/TaQL/TableExprId.h000066400000000000000000000125751321422335000176720ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #ifndef TABLES_TABLEEXPRID_H #define TABLES_TABLEEXPRID_H //# Includes #include #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 (uInt 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 (uInt 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 (uInt 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 (uInt 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-2.4.1/tables/TaQL/TableExprIdAggr.h000066400000000000000000000112061321422335000204610ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id: TableExprId.h 21403 2013-12-03 20:51:02Z gervandiepen $ #ifndef TABLES_TABLEEXPRIDAGGR_H #define TABLES_TABLEEXPRIDAGGR_H //# Includes #include #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 CountedPtr& result) : itsMagicValue (0xabababab), itsResult (result) {} // Get out the aggregation result object. const TableExprGroupResult& result() const { return *itsResult; } // Get the magic value (to check if it correct). uInt getMagicValue() const { return itsMagicValue; } // Cast a TableExprId object to TableExprIdAggr. It check if the cast // if 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; CountedPtr itsResult; }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/tables/TaQL/TableGram.cc000066400000000000000000000117031321422335000175130ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ // 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 //# Define register as empty string to avoid warnings in C++11 compilers //# because keyword register is not supported anymore. #define register #include "TableGram.ycc" // bison output #include "TableGram.lcc" // flex output // 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; //# Parse the command. //# Do a yyrestart(yyin) first to make the flex scanner reentrant. int tableGramParseCommand (const String& command) { TableGramrestart (TableGramin); yy_start = 1; // Save global state for re-entrancy. const char* savStrpTableGram = strpTableGram; Int savPosTableGram= posTableGram; strpTableGram = command.chars(); // get pointer to command string posTableGram = 0; // initialize string position int sts = TableGramparse(); // parse command string // Restore global state. strpTableGram = savStrpTableGram; posTableGram= savPosTableGram; 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 (TableError ("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 (TableError ("invalid date string " + in)); } return MVTime(res); } } //# NAMESPACE CASACORE - END casacore-2.4.1/tables/TaQL/TableGram.h000066400000000000000000000063741321422335000173650ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 escaped characters. String tableGramRemoveEscapes (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-2.4.1/tables/TaQL/TableGram.ll000066400000000000000000000563701321422335000175460ustar00rootroot00000000000000/* TableGram.l: 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: aips2-request@nrao.edu. Postal address: AIPS++ Project Office National Radio Astronomy Observatory 520 Edgemont Road Charlottesville, VA 22903-2475 USA $Id$ */ /* 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 */ %s STYLEstate %s EXPRstate %s GIVINGstate %s FROMstate %s CRETABstate /* exclusive state */ %x SHOWstate /* 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 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. */ WHITE1 [ \t\n] WHITE {WHITE1}* 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]+ 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}([-/ ]{DTIME})? 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})+ 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]) 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])|{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}[Tt][Aa][Bb][Ll][Ee]{WHITE1} ALTERTAB [Aa][Ll][Tt][Ee][Rr]{WHITE}[Tt][Aa][Bb][Ll][Ee]{WHITE1} ADDCOL ,?{WHITE}{ADD}{WHITE}{COLUMN}{WHITE1} RENAMECOL ,?{WHITE}{RENAME}{WHITE}{COLUMN}{WHITE1} DROPCOL ,?{WHITE}{DROP}{WHITE}{COLUMN}{WHITE1} SETKEY ,?{WHITE}{SET}{WHITE}{KEYWORD}{WHITE1} COPYKEY ,?{WHITE}{COPY}{WHITE}{KEYWORD}{WHITE1} RENAMEKEY ,?{WHITE}{RENAME}{WHITE}{KEYWORD}{WHITE1} DROPKEY ,?{WHITE}{DROP}{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] 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] ALLFUNC {ALL}{WHITE}"(" NAME \\?[A-Za-z_]([A-Za-z_0-9]|(\\.))* NAMEFLD ({NAME}".")?{NAME}?("::")?{NAME}("."{NAME})* TEMPTAB [$]{INT} NAMETAB ([A-Za-z0-9_./+\-~$@:]|(\\.))+ UDFLIBSYN {NAME}{WHITE}"="{WHITE}{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}) %% /* The command to be analyzed is: 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 field name. A table name can be given in the FROM part and in the giving PART. These are indicated by the FROM/GIVING/CRETABstate, because a table name can contain special characters like -. In the FROMstate a table name can also be $nnn indicating a temporary table. In a subquery care must be taken that the state is switched back to EXPRstate, because a FROM can be the last part in a subquery and because a set can be specified in the GIVING part. This is done by setting the state when parentheses or square brackets are found. ( and [ indicate the beginning of a set(subquery). ) and ] indicate the end of subquery. */ /* In 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(FROMstate); return UPDATE; } {SET} { tableGramPosition() += yyleng; BEGIN(EXPRstate); return UPDSET; } {INSERT} { tableGramPosition() += yyleng; BEGIN(FROMstate); 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(CRETABstate); return CREATETAB; } {ALTERTAB} { tableGramPosition() += yyleng; BEGIN(CRETABstate); return ALTERTAB; } {ADDCOL} { tableGramPosition() += yyleng; BEGIN(EXPRstate); return ADDCOL; } {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} { BEGIN(EXPRstate); tableGramPosition() += yyleng; return ADDROW; } {DMINFO} { tableGramPosition() += yyleng; BEGIN(EXPRstate); return DMINFO; } {FROM} { tableGramPosition() += yyleng; BEGIN(FROMstate); return FROM; } {WHERE} { tableGramPosition() += yyleng; BEGIN(EXPRstate); theFromQueryDone = False; 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(GIVINGstate); return GIVING; } {INTO} { tableGramPosition() += yyleng; return INTO; } {SUBTABLES} { tableGramPosition() += yyleng; 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; throw (TableInvExpr ("JOIN ON is not supported yet")); } {ON} { tableGramPosition() += yyleng; throw (TableInvExpr ("JOIN ON is not supported yet")); } {AS} { tableGramPosition() += yyleng; return AS; } {TO} { tableGramPosition() += yyleng; return TO; } {IN} { tableGramPosition() += yyleng; return IN; } {INCONE} { tableGramPosition() += yyleng; return INCONE; } "[" { tableGramPosition() += yyleng; BEGIN(EXPRstate); return LBRACKET; } "(" { tableGramPosition() += yyleng; BEGIN(EXPRstate); return LPAREN; } "]" { tableGramPosition() += yyleng; BEGIN(EXPRstate); return RBRACKET; } ")" { tableGramPosition() += yyleng; BEGIN(EXPRstate); 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 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; } {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; if (theFromQueryDone) { BEGIN(FROMstate); theFromQueryDone=False; } 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 the Exprstate 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 . will 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). */ {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 can be given in the FROM clause */ {TEMPTAB} { tableGramPosition() += yyleng; Int64 ival = atoi(TableGramtext+1); lvalp->val = new TaQLConstNode(new TaQLConstNodeRep (ival)); TaQLNode::theirNodesCreated.push_back (lvalp->val); return TABNAME; } /* A table file name can be given in the UPDATE, FROM, GIVING, CRETAB clause */ {NAMETAB} { tableGramPosition() += yyleng; lvalp->val = new TaQLConstNode( new TaQLConstNodeRep (tableGramRemoveEscapes (TableGramtext))); TaQLNode::theirNodesCreated.push_back (lvalp->val); return TABNAME; } /* Whitespace is skipped */ {WHITE} { tableGramPosition() += yyleng; } /* Comment is skipped */ {COMMENT} { tableGramPosition() += yyleng; } /* An unterminated string is an error */ {USTRING} { throw (TableInvExpr ("Unterminated string")); } /* terminate on EOF */ <> { yyterminate(); } /* Any other character is invalid */ . { return YYERRCODE; } %% casacore-2.4.1/tables/TaQL/TableGram.yy000066400000000000000000001704011321422335000175700ustar00rootroot00000000000000/* 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: aips2-request@nrao.edu. Postal address: AIPS++ Project Office National Radio Astronomy Observatory 520 Edgemont Road Charlottesville, VA 22903-2475 USA $Id$ */ %{ 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 2 %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 FROM %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 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 EXISTS %token LIKE %token ILIKE %token LPAREN %token RPAREN %token COMMA %token LBRACKET %token RBRACKET %token LBRACE %token RBRACE %token COLON %token SEMICOL %token OPENOPEN %token OPENCLOSED %token CLOSEDOPEN %token CLOSEDCLOSED %token OPENEMPTY %token EMPTYOPEN %token CLOSEDEMPTY %token EMPTYCLOSED %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 calccomm %type nestedcomm %type countcomm %type cretabcomm %type alttabcomm %type tfcommand %type subquery %type selrow %type selcol %type normcol %type tables %type tabconc %type concsub %type concslist %type concinto %type whexpr %type groupby %type exprlist %type having %type order %type limitoff %type tabnmtyps %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 updrow %type updlist %type updexpr %type insrow %type insclist %type insvalue %type insparts %type inspart %type insvlist %type altcomm %type altlist %type rencols %type dropcols %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 /* Alas you cannot use objects in a union, so pointers have to be used. This is causing problems in cleaning up in case of a parse error. Hence a vector (in TaQLNode) is used to keep track of the nodes created. They are deleted at the end of the parsing. */ %union { TaQLConstNode* val; TaQLRegexNode* valre; TaQLNode* node; TaQLConstNode* nodename; TaQLMultiNode* nodelist; TaQLQueryNode* nodeselect; TaQLRecFldNodeRep* noderecfldrep; } %{ namespace casacore { //# NAMESPACE CASACORE - BEGIN Bool theFromQueryDone; /* for flex for knowing how to handle a , */ } //# NAMESPACE CASACORE - END int TableGramlex (YYSTYPE*); %} %% /* A command can optionally be ended with a semicolon */ topcomm: topcomm1 | topcomm1 SEMICOL ; 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); } ; stylecoms: stylecoms stylecomm | stylecomm ; stylecomm: STYLE stylelist ; 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()); } ; command: selcomm { TaQLNode::theirNode = *$1; } | updcomm { TaQLNode::theirNode = *$1; } | inscomm { TaQLNode::theirNode = *$1; } | delcomm { TaQLNode::theirNode = *$1; } | calccomm { TaQLNode::theirNode = *$1; } | nestedcomm { TaQLNode::theirNode = *$1; } | showcomm { TaQLNode::theirNode = *$1; } ; showcomm: SHOW showlist { $$ = new TaQLNode(new TaQLShowNodeRep (*$2)); TaQLNode::theirNodesCreated.push_back ($$); } ; showlist: { /* no list */ $$ = new TaQLMultiNode(); TaQLNode::theirNodesCreated.push_back ($$); } | showflds { $$ = $1; } 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; } ; tfcommand: subquery { $$ = $1; } | LPAREN nestedcomm RPAREN { $$ = $2; $$->setBrackets(); } | LBRACKET nestedcomm RBRACKET { $$ = $2; $$->setBrackets(); } ; subquery: LPAREN selcomm RPAREN { $$ = $2; $$->setBrackets(); } | LBRACKET selcomm RBRACKET { $$ = $2; $$->setBrackets(); } ; selcomm: SELECT selrow { $$ = $2; } selrow: selcol FROM tables whexpr groupby having order limitoff given dminfo { $$ = new TaQLQueryNode( new TaQLSelectNodeRep (*$1, *$3, 0, *$4, *$5, *$6, *$7, *$8, *$9, *$10)); TaQLNode::theirNodesCreated.push_back ($$); } | selcol into FROM tables whexpr groupby having order limitoff dminfo { $$ = new TaQLQueryNode( new TaQLSelectNodeRep (*$1, *$4, 0, *$5, *$6, *$7, *$8, *$9, *$2, *$10)); TaQLNode::theirNodesCreated.push_back ($$); } | selcol whexpr groupby having order limitoff given dminfo { $$ = new TaQLQueryNode( new TaQLSelectNodeRep (*$1, *$2, *$3, *$4, *$5, *$6, *$7, *$8)); TaQLNode::theirNodesCreated.push_back ($$); } | selcol into whexpr groupby having order limitoff dminfo { $$ = new TaQLQueryNode( new TaQLSelectNodeRep (*$1, *$3, *$4, *$5, *$6, *$7, *$2, *$8)); TaQLNode::theirNodesCreated.push_back ($$); } ; 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 ($$); } countcomm: COUNT normcol FROM tables whexpr { $$ = new TaQLQueryNode( new TaQLCountNodeRep (*$2, *$4, *$5)); TaQLNode::theirNodesCreated.push_back ($$); } ; updcomm: UPDATE updrow { $$ = $2; } ; updrow: tables UPDSET updlist FROM tables whexpr order limitoff { $$ = new TaQLNode( new TaQLUpdateNodeRep (*$1, *$3, *$5, *$6, *$7, *$8)); TaQLNode::theirNodesCreated.push_back ($$); } | tables UPDSET updlist whexpr order limitoff { $$ = new TaQLNode( new TaQLUpdateNodeRep (*$1, *$3, 0, *$4, *$5, *$6)); TaQLNode::theirNodesCreated.push_back ($$); } ; updlist: updlist COMMA updexpr { $$ = $1; $$->add (*$3); } | updexpr { $$ = new TaQLMultiNode(False); TaQLNode::theirNodesCreated.push_back ($$); $$->add (*$1); } ; 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 ($$); } ; inscomm: INSERT insrow { $$ = $2; } ; insrow: INTO tables insclist selcomm { /* insert with SELECT command */ $4->setNoExecute(); $$ = new TaQLNode( new TaQLInsertNodeRep (*$2, *$3, *$4, TaQLNode())); TaQLNode::theirNodesCreated.push_back ($$); } | INTO tables insclist insvalue { /* insert in SQL style */ $$ = new TaQLNode( new TaQLInsertNodeRep (*$2, *$3, *$4, TaQLNode())); TaQLNode::theirNodesCreated.push_back ($$); } | LIMIT orexpr INTO tables insclist insvalue { $$ = new TaQLNode( new TaQLInsertNodeRep (*$4, *$5, *$6, *$2)); TaQLNode::theirNodesCreated.push_back ($$); } | INTO tables insclist insvalue LIMIT orexpr { $$ = new TaQLNode( new TaQLInsertNodeRep (*$2, *$3, *$4, *$6)); TaQLNode::theirNodesCreated.push_back ($$); } | INTO tables UPDSET updlist { /* insert in update style */ $$ = new TaQLNode( new TaQLInsertNodeRep (*$2, *$4)); TaQLNode::theirNodesCreated.push_back ($$); } ; 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; } ; insparts: insparts COMMA inspart { $$ = $1; $$->add (*$3); } | inspart { $$ = new TaQLMultiNode(False); TaQLNode::theirNodesCreated.push_back ($$); $$->add (*$1); } 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); } ; delcomm: DELETE FROM tables whexpr order limitoff { $$ = new TaQLNode( new TaQLDeleteNodeRep (*$3, *$4, *$5, *$6)); TaQLNode::theirNodesCreated.push_back ($$); } ; calccomm: CALC FROM tables CALC orexpr { $$ = new TaQLNode( new TaQLCalcNodeRep (*$3, *$5, TaQLNode(), TaQLNode(), TaQLNode())); TaQLNode::theirNodesCreated.push_back ($$); } | CALC orexpr { TaQLMultiNode tabNode((TaQLMultiNodeRep*)0); $$ = new TaQLNode( new TaQLCalcNodeRep (tabNode, *$2, TaQLNode(), TaQLNode(), TaQLNode())); TaQLNode::theirNodesCreated.push_back ($$); } | CALC orexpr FROM tables whexpr order limitoff { $$ = new TaQLNode( new TaQLCalcNodeRep (*$4, *$2, *$5, *$6, *$7)); TaQLNode::theirNodesCreated.push_back ($$); } cretabcomm: CREATETAB tabnmtyp colspecs nrowspec dminfo { $$ = new TaQLQueryNode( new TaQLCreTabNodeRep (*$2, *$3, *$4, *$5)); TaQLNode::theirNodesCreated.push_back ($$); } | CREATETAB tabnmtyp LPAREN colspecs RPAREN nrowspec dminfo { $$ = new TaQLQueryNode( new TaQLCreTabNodeRep (*$2, *$4, *$6, *$7)); TaQLNode::theirNodesCreated.push_back ($$); } | CREATETAB tabnmtyp LBRACKET colspecs RBRACKET nrowspec dminfo { $$ = new TaQLQueryNode( new TaQLCreTabNodeRep (*$2, *$4, *$6, *$7)); TaQLNode::theirNodesCreated.push_back ($$); } ; alttabcomm: ALTERTAB tabalias altlist { $$ = new TaQLQueryNode( new TaQLAltTabNodeRep (*$2, TaQLMultiNode(), *$3)); TaQLNode::theirNodesCreated.push_back ($$); } | ALTERTAB tabalias FROM tables altlist { $$ = new TaQLQueryNode( new TaQLAltTabNodeRep (*$2, *$4, *$5)); TaQLNode::theirNodesCreated.push_back ($$); } ; altlist: altlist altcomm { $$ = $1; $$->add (*$2); } | altcomm { $$ = new TaQLMultiNode(False); TaQLNode::theirNodesCreated.push_back ($$); $$->setSeparator (" "); $$->add (*$1); } ; altcomm: ADDCOL colspecs dminfo { $$ = new TaQLNode ( new TaQLAddColNodeRep(*$2, *$3)); TaQLNode::theirNodesCreated.push_back ($$); } | RENAMECOL rencols { $$ = new TaQLNode( new TaQLRenDropNodeRep(0, *$2)); TaQLNode::theirNodesCreated.push_back ($$); } | DROPCOL dropcols { $$ = 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 ($$); } ; 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())); } ; dropcols: dropcols COMMA NAME { $$ = $1; $$->add (new TaQLKeyColNodeRep ($3->getString())); } | NAME { $$ = new TaQLMultiNode(False); TaQLNode::theirNodesCreated.push_back ($$); $$->add (new TaQLKeyColNodeRep ($1->getString())); } ; 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())); } ; dropkeys: dropkeys COMMA namefld { $$ = $1; $$->add (new TaQLKeyColNodeRep ($3->getString())); } | namefld { $$ = new TaQLMultiNode(False); TaQLNode::theirNodesCreated.push_back ($$); $$->add (new TaQLKeyColNodeRep ($1->getString())); } ; 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; } copykeys: copykeys COMMA copykey { $$ = $1; $$->add (*$3); } | copykey { $$ = new TaQLMultiNode(False); TaQLNode::theirNodesCreated.push_back ($$); $$->add (*$1); } copykey: namefld EQASS namefld asdtype { $$ = new TaQLNode( new TaQLRecFldNodeRep($1->getString(), $3->getString(), $4->getString())); TaQLNode::theirNodesCreated.push_back ($$); } keyval: srecval { $$ = $1; } | brackval { $$ = $1; } 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 given datatype */ $$ = new TaQLRecFldNodeRep ("", TaQLNode(), $4->getString()); } dminfo: { /* no datamans */ $$ = new TaQLMultiNode(); TaQLNode::theirNodesCreated.push_back ($$); } | DMINFO dmlist { $$ = $2; } ; exprlist: exprlist COMMA orexpr { $$ = $1; $$->add (*$3); } | orexpr { $$ = new TaQLMultiNode(False); TaQLNode::theirNodesCreated.push_back ($$); $$->add (*$1); } ; 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 ($$); } ; having: { /* no having */ $$ = new TaQLNode(); TaQLNode::theirNodesCreated.push_back ($$); } | HAVING orexpr { $$ = $2; } ; 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 ($$); } ; 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 ($$); } ; tabnmtyp: tabname { $$ = new TaQLNode( new TaQLGivingNodeRep ($1->getString(), TaQLMultiNode())); TaQLNode::theirNodesCreated.push_back ($$); } | tabname AS tabnmtyps { $$ = new TaQLNode( new TaQLGivingNodeRep ($1->getString(), *$3)); TaQLNode::theirNodesCreated.push_back ($$); } | AS tabnmtyps { $$ = new TaQLNode( new TaQLGivingNodeRep ("", *$2)); TaQLNode::theirNodesCreated.push_back ($$); } /* | tabname LIKE tabname { $$ = new TaQLNode( new TaQLGivingNodeRep ($1->getString(), TaQLMultiNode())); TaQLNode::theirNodesCreated.push_back ($$); } | tabname LIKE tabname AS tabnmtyps { $$ = new TaQLNode( new TaQLGivingNodeRep ($1->getString(), *$5)); TaQLNode::theirNodesCreated.push_back ($$); } | tabname FROM tabname { $$ = new TaQLNode( new TaQLGivingNodeRep ($1->getString(), TaQLMultiNode())); TaQLNode::theirNodesCreated.push_back ($$); } | tabname FROM tabname AS tabnmtyps { $$ = new TaQLNode( new TaQLGivingNodeRep ($1->getString(), *$5)); TaQLNode::theirNodesCreated.push_back ($$); } */ ; tabnmtyps: 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; } ; given: { /* no result */ $$ = new TaQLNode(); TaQLNode::theirNodesCreated.push_back ($$); } | GIVING tabnmtyp { $$ = $2; } | GIVING LBRACKET elems RBRACKET { $$ = new TaQLNode( new TaQLGivingNodeRep (*$3)); TaQLNode::theirNodesCreated.push_back ($$); } ; into: INTO tabnmtyp { $$ = $2; } ; columns: { /* no column names given (thus take all) */ $$ = new TaQLMultiNode(); TaQLNode::theirNodesCreated.push_back ($$); } | collist { $$ = $1; } collist: colexpr { $$ = new TaQLMultiNode(False); TaQLNode::theirNodesCreated.push_back ($$); $$->add (*$1); } | collist COMMA colexpr { $$ = $1; $$->add (*$3); } ; 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 { $$ = 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; } ; 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 ($$); } ; 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())); } ; nrowspec: { /* no nrows given */ $$ = new TaQLNode(); TaQLNode::theirNodesCreated.push_back ($$); } | LIMIT orexpr { $$ = $2; } ; colspecs: { /* no column specifications given */ $$ = new TaQLMultiNode(); TaQLNode::theirNodesCreated.push_back ($$); } | colspecl { $$ = $1; } colspecl: colspec { $$ = new TaQLMultiNode(False); TaQLNode::theirNodesCreated.push_back ($$); $$->add (*$1); } | colspecl COMMA colspec { $$ = $1; $$->add (*$3); } ; colspec: NAME NAME { $$ = new TaQLNode( new TaQLColSpecNodeRep($1->getString(), $2->getString(), TaQLMultiNode())); TaQLNode::theirNodesCreated.push_back ($$); } | NAME NAME srecfield { TaQLMultiNode re(False); re.add (*$3); $$ = new TaQLNode( new TaQLColSpecNodeRep($1->getString(), $2->getString(), re)); TaQLNode::theirNodesCreated.push_back ($$); } | NAME NAME LBRACKET recexpr RBRACKET { $$ = new TaQLNode( new TaQLColSpecNodeRep($1->getString(), $2->getString(), *$4)); TaQLNode::theirNodesCreated.push_back ($$); } /* | NAME LIKE namefld { $$ = new TaQLNode( new TaQLColSpecNodeRep($1->getString(), $3->getString(), TaQLMultiNode())); TaQLNode::theirNodesCreated.push_back ($$); } | NAME LIKE namefld srecfield { TaQLMultiNode re(False); re.add (*$4); $$ = new TaQLNode( new TaQLColSpecNodeRep($1->getString(), $3->getString(), re)); TaQLNode::theirNodesCreated.push_back ($$); } | NAME LIKE namefld LBRACKET recexpr RBRACKET { $$ = new TaQLNode( new TaQLColSpecNodeRep($1->getString(), $3->getString(), *$5)); TaQLNode::theirNodesCreated.push_back ($$); } */ ; tables: tabalias { $$ = new TaQLMultiNode(False); TaQLNode::theirNodesCreated.push_back ($$); $$->add (*$1); } | tables COMMA tabalias { $$ = $1; $$->add (*$3); } ; /* If NAME is given, it is purely alphanumeric, so it can be used as alias. This is not the case if another type of name is given, so in that case there is no alias. Hence the 2 cases have to be handled differently. */ 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 { $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 ($$); } ; tfnamen: tfname { $$ = $1; } | NAME { $1->setIsTableName(); $$ = $1; } ; tfname: tfcommand { theFromQueryDone = True; $1->setFromExecute(); $$ = $1; } | stabname { $$ = $1; } | LBRACKET tabconc concsub concinto RBRACKET { $$ = new TaQLNode( new TaQLConcTabNodeRep($4->getString(), *$2, *$3)); TaQLNode::theirNodesCreated.push_back ($$); } ; concsub: { /* no SUBTABLES */ $$ = new TaQLMultiNode(); TaQLNode::theirNodesCreated.push_back ($$); } | SUBTABLES concslist { $$ = $2; } ; concslist: NAME { $$ = new TaQLMultiNode(False); TaQLNode::theirNodesCreated.push_back ($$); $1->setIsTableName(); $$->add (*$1); } | concslist COMMA NAME { $$ = $1; $3->setIsTableName(); $$->add (*$3); } ; concinto: { /* no GIVING */ $$ = new TaQLConstNode(new TaQLConstNodeRep(String())); TaQLNode::theirNodesCreated.push_back ($$); } | GIVING tabname { $$ = $2; } | INTO tabname { $$ = $2; } ; stabname: TABNAME { $1->setIsTableName(); $$ = $1; } | FLDNAME { $1->setIsTableName(); $$ = $1; } | STRINGLITERAL { $1->setIsTableName(); $$ = $1; } ; tabname: NAME { $1->setIsTableName(); $$ = $1; } | stabname { $$ = $1; } ; tabconc: tabalias { $$ = new TaQLMultiNode(False); TaQLNode::theirNodesCreated.push_back ($$); $$->add (*$1); } | tabconc COMMA tabalias { $$ = $1; $$->add (*$3); } ; whexpr: { /* no selection */ $$ = new TaQLNode(); TaQLNode::theirNodesCreated.push_back ($$); } | WHERE orexpr { $$ = $2; } ; orexpr: andexpr { $$ = $1; } | orexpr OR andexpr { $$ = new TaQLNode( new TaQLBinaryNodeRep (TaQLBinaryNodeRep::B_OR, *$1, *$3)); TaQLNode::theirNodesCreated.push_back ($$); } ; andexpr: relexpr { $$ = $1; } | andexpr AND relexpr { $$ = new TaQLNode( new TaQLBinaryNodeRep (TaQLBinaryNodeRep::B_AND, *$1, *$3)); TaQLNode::theirNodesCreated.push_back ($$); } ; 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 { 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 { 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 { $$ = 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 { 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 { $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 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 ($$); } ; 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 ($$); } ; simexpr: inxexpr { $$ = $1; } | inxexpr unit { $$ = new TaQLNode( new TaQLUnitNodeRep ($2->getString(), *$1)); TaQLNode::theirNodesCreated.push_back ($$); } ; inxexpr: simbexpr { $$ = $1; } | simbexpr LBRACKET subscripts RBRACKET { $$ = new TaQLNode( new TaQLBinaryNodeRep (TaQLBinaryNodeRep::B_INDEX, *$1, *$3)); TaQLNode::theirNodesCreated.push_back ($$); } ; simbexpr: LPAREN orexpr RPAREN { $$ = $2; } | namefld LPAREN elemlist RPAREN { $$ = new TaQLNode( new TaQLFuncNodeRep ($1->getString(), *$3)); TaQLNode::theirNodesCreated.push_back ($$); } | COUNT LPAREN elemlist RPAREN { $$ = new TaQLNode( new TaQLFuncNodeRep ("COUNT", *$3)); TaQLNode::theirNodesCreated.push_back ($$); } | COUNTALL { $$ = new TaQLNode( new TaQLFuncNodeRep ("COUNTALL")); TaQLNode::theirNodesCreated.push_back ($$); } | namefld { $$ = new TaQLNode( new TaQLKeyColNodeRep ($1->getString())); TaQLNode::theirNodesCreated.push_back ($$); } | literal { $$ = $1; } | set { $$ = $1; } ; namefld: NAME { /* simple name */ $$ = $1; } | FLDNAME { /* name with . or :: */ $$ = $1; } ; unit: namefld { $$ = $1; } | STRINGLITERAL { /* compound unit (with special characters) */ $$ = $1; } ; literal: LITERAL { $$ = $1; } | STRINGLITERAL { $$ = $1; } ; set: LBRACKET elems RBRACKET { $2->setIsSetOrArray(); $$ = $2; } | LPAREN elems RPAREN { $2->setIsSetOrArray(); $$ = $2; } | subquery { $$ = $1; } ; elemlist: elems { $$ = $1; $$->setPPFix("", ""); } | { $$ = new TaQLMultiNode(False); /* no elements */ TaQLNode::theirNodesCreated.push_back ($$); } ; elems: elems COMMA elem { $$ = $1; $$->add (*$3); } | elem { $$ = new TaQLMultiNode(False); TaQLNode::theirNodesCreated.push_back ($$); $$->setPPFix ("[", "]"); $$->add (*$1); } ; elem: orexpr { $$ = $1; } | range { $$ = $1; } ; singlerange: range { $$ = new TaQLMultiNode(True); TaQLNode::theirNodesCreated.push_back ($$); $$->add (*$1); } ; range: colonrangeinterval { $$ = $1; } | 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 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 ($$); } ; 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); } ; subsingle: orexpr { $$ = new TaQLNode( new TaQLIndexNodeRep (*$1, 0, 0)); TaQLNode::theirNodesCreated.push_back ($$); } | colonrangeindex { $$ = $1; } ; subsrange: arithexpr { $$ = new TaQLNode( new TaQLIndexNodeRep (*$1, 0, 0)); TaQLNode::theirNodesCreated.push_back ($$); } | colonrangeindex { $$ = $1; } ; 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 ($$); } 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 ($$); } 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 ($$); } ; sortlist : sortlist COMMA sortexpr { $$ = $1; $$->add (*$3); } | sortexpr { $$ = new TaQLMultiNode(False); TaQLNode::theirNodesCreated.push_back ($$); $$->add (*$1); } ; 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 ($$); } ; dmlist: dmelem { $$ = new TaQLMultiNode(False); TaQLNode::theirNodesCreated.push_back ($$); $$->add (*$1); } | dmlist COMMA dmelem { $$ = $1; $$->add (*$3); } ; dmelem: LBRACKET recexpr RBRACKET { $$ = new TaQLNode( new TaQLRecFldNodeRep ("", *$2, "")); TaQLNode::theirNodesCreated.push_back ($$); } ; recexpr: recexpr COMMA recfield { $$->add (*$3); } | recfield { $$ = new TaQLMultiNode(False); TaQLNode::theirNodesCreated.push_back ($$); $$->setPPFix ("[", "]"); $$->add (*$1); } ; recfield: srecfield { $$ = $1; } | rrecfield { $$ = $1; } ; srecfield: NAME EQASS srecval { $$ = new TaQLNode( new TaQLRecFldNodeRep ($1->getString(), *$3)); TaQLNode::theirNodesCreated.push_back ($$); delete $3; } ; srecval: orexpr asdtype { $$ = new TaQLRecFldNodeRep ("", *$1, $2->getString()); } rrecfield: NAME EQASS brackval { $$ = new TaQLNode( new TaQLRecFldNodeRep ($1->getString(), *$3)); TaQLNode::theirNodesCreated.push_back ($$); delete $3; } ; asdtype: { /* no datatype */ $$ = new TaQLConstNode(new TaQLConstNodeRep(String())); TaQLNode::theirNodesCreated.push_back ($$); } | AS NAME { $$ = $2; } ; %% casacore-2.4.1/tables/TaQL/TableParse.cc000066400000000000000000004216701321422335000177070ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: TableParse.cc 21399 2013-11-12 07:55:35Z gervandiepen $ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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. TableParse::TableParse() {} //# Constructor with given table name and possible shorthand. TableParse::TableParse (const Table& table, const String& shorthand) : shorthand_p (shorthand), table_p (table) {} TableParse::TableParse (const TableParse& that) : shorthand_p (that.shorthand_p), table_p (that.table_p) {} TableParse& TableParse::operator= (const TableParse& that) { if (this != &that) { shorthand_p = that.shorthand_p; table_p = that.table_p; } return *this; } 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) { TableParseSelect::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) { TableParseSelect::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); TableParseSelect::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.nelements() == 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); } } TableParseUpdate::~TableParseUpdate() { // indexPtr_p does not need to be deleted because it is part of indexNode_p. } TableParseSort::TableParseSort() : order_p (Sort::Ascending), given_p (False) {} TableParseSort::TableParseSort (const TableExprNode& node) : node_p (node), order_p (Sort::Ascending), given_p (False) { checkNode(); } TableParseSort::TableParseSort (const TableExprNode& node, Sort::Order order) : node_p (node), order_p (order), given_p (True) { checkNode(); } TableParseSort::~TableParseSort() {} void TableParseSort::checkNode() const { if (! node_p.isScalar()) { throw TableInvExpr("ORDERBY column/expression must be a scalar"); } TableParseSelect::checkAggrFuncs (node_p); } TableParseSelect::TableParseSelect (CommandType commandType) : commandType_p (commandType), nrSelExprUsed_p (0), distinct_p (False), resultType_p (0), resultCreated_p (False), endianFormat_p (Table::AipsrcEndian), overwrite_p (True), resultSet_p (0), groupbyRollup_p (False), limit_p (0), endrow_p (0), offset_p (0), stride_p (1), insSel_p (0), noDupl_p (False), order_p (Sort::Ascending) {} TableParseSelect::~TableParseSelect() { // Note that insSel_p is simply a pointer to another object, // so no delete should be done. delete resultSet_p; for (uInt i=0; i tempTables, const vector& stack) { Table table = makeTable (tabnr, name, ftab, shorthand, tempTables, stack); fromTables_p.push_back (TableParse(table, shorthand)); } Table TableParseSelect::makeTable (Int tabnr, const String& name, const Table& ftab, const String& shorthand, const vector tempTables, const vector& stack, Bool alwaysOpen) { Table table; //# If the table name is numeric, we have a temporary table number //# which will be made 0-based. //# Find it in the block of temporary tables. if (tabnr >= 0) { tabnr -= 1; if (tabnr < 0 || tabnr >= Int(tempTables.size()) || tempTables[tabnr] == 0) { throw (TableInvExpr ("Invalid temporary table number given")); } table = *(tempTables[tabnr]); } else if (! ftab.isNull()) { //# The table is a temporary table (from a select clause). table = ftab; } else { //# The table name is a string. //# When the name contains ::, it is a keyword in a table at an outer //# SELECT statement. String shand, columnName; Vector fieldNames; if (splitName (shand, columnName, fieldNames, name, False, False, True)) { table = tableKey (name, shand, columnName, fieldNames, stack); } else { // If no or equal shorthand is given, try to see if the // given name is already used as a shorthand. // If so, take the table of that shorthand. Bool foundSH = False; if (shorthand.empty() || name == shorthand) { for (Int i=stack.size()-1; i>=0; i--) { Table tab = stack[i]->findTable (name); if (! tab.isNull()) { table = tab; foundSH = True; break; } } } if (!foundSH && alwaysOpen) { table = Table::openTable(name); } } } return table; } void TableParseSelect::replaceTable (const Table& table) { AlwaysAssert (!fromTables_p.empty(), AipsError); // Replace table, but use same shorthand. fromTables_p[0] = TableParse(table, fromTables_p[0].shorthand()); } Table TableParseSelect::tableKey (const String& name, const String& shorthand, const String& columnName, const Vector& fieldNames, const vector& stack) { //# Try to find the given shorthand on all levels. for (Int i=stack.size()-1; i>=0; i--) { Table tab = stack[i]->findTable (shorthand); if (! tab.isNull()) { Table result = findTableKey (tab, columnName, fieldNames); if (! result.isNull()) { return result; } } } // Apparently it is no keyword in an outer table. // Try to open the table using subtables by splitting at the ::. return Table::openTable (name); } Table TableParseSelect::findTableKey (const Table& table, const String& columnName, const Vector& fieldNames) { //# Pick the table or column keyword set. if (columnName.empty() || table.tableDesc().isColumn (columnName)) { if (columnName.empty() && fieldNames.empty()) { return table; } const TableRecord* keyset = columnName.empty() ? &(table.keywordSet()) : &(TableColumn (table, columnName).keywordSet()); // All fieldnames, except last one, should be records. uInt last = fieldNames.nelements() - 1; for (uInt i=0; ifieldNumber (fieldNames(i)); if (fieldnr < 0 || keyset->dataType(fieldnr) != TpRecord) { return Table(); } keyset = &(keyset->subRecord(fieldnr)); } //# If the keyword exists and is a table, take it. Int fieldnr = keyset->fieldNumber (fieldNames(last)); if (fieldnr >= 0 && keyset->dataType(fieldnr) == TpTable) { return keyset->asTable (fieldnr); } } //# Not found. 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.l 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 TableParseSelect::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.nelements()) { 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.nelements() == 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.nelements() - stfld); for (uInt i=stfld; i 0) { return fromTables_p[0].table(); } } else { for (uInt i=0; i fieldNames; Bool hasKey = splitName (shand, columnName, fieldNames, name, True, False, False); //# Use first table if there is no shorthand given. //# Otherwise find the table. Table tab = findTable (shand); if (tab.isNull()) { throw (TableInvExpr("Shorthand " + shand + " has not been defined")); 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 (!projectExprTable_p.isNull() && projectExprTable_p.tableDesc().isColumn (columnName)) { uInt nc = projectExprSubset_p.size(); projectExprSubset_p.resize (nc+1); projectExprSubset_p[nc] = inx; return projectExprTable_p.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. if (tab.tableDesc().isColumn (columnName)) { 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(tab.keyCol (columnName, fieldNames)); 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); } TableExprNode TableParseSelect::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.nelements() == 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. TableExprFuncNode::FunctionType TableParseSelect::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") { 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 == "product") { ftype = TableExprFuncNode::arrproductFUNC; } else if (funcName == "products") { ftype = TableExprFuncNode::arrproductsFUNC; } else if (funcName == "sumsqr" || funcName == "sumsquare") { ftype = TableExprFuncNode::arrsumsqrFUNC; } else if (funcName == "sumsqrs" || funcName == "sumsquares") { ftype = TableExprFuncNode::arrsumsqrsFUNC; } 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::arrvarianceFUNC; } else if (funcName == "variances") { ftype = TableExprFuncNode::arrvariancesFUNC; } else if (funcName == "runningvariance") { ftype = TableExprFuncNode::runvarianceFUNC; } else if (funcName == "boxedvariance") { ftype = TableExprFuncNode::boxvarianceFUNC; } else if (funcName == "stddev") { ftype = TableExprFuncNode::arrstddevFUNC; } else if (funcName == "stddevs") { ftype = TableExprFuncNode::arrstddevsFUNC; } else if (funcName == "runningstddev") { ftype = TableExprFuncNode::runstddevFUNC; } else if (funcName == "boxedstddev") { ftype = TableExprFuncNode::boxstddevFUNC; } 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 == "any") { ftype = TableExprFuncNode::anyFUNC; } else if (funcName == "anys") { ftype = TableExprFuncNode::anysFUNC; } else if (funcName == "runningany") { ftype = TableExprFuncNode::runanyFUNC; } else if (funcName == "boxedany") { ftype = TableExprFuncNode::boxanyFUNC; } else if (funcName == "all") { ftype = TableExprFuncNode::allFUNC; } else if (funcName == "alls") { ftype = TableExprFuncNode::allsFUNC; } else if (funcName == "runningall") { ftype = TableExprFuncNode::runallFUNC; } else if (funcName == "boxedall") { ftype = TableExprFuncNode::boxallFUNC; } else if (funcName == "ntrue") { ftype = TableExprFuncNode::ntrueFUNC; } else if (funcName == "ntrues") { ftype = TableExprFuncNode::ntruesFUNC; } else if (funcName == "nfalse") { ftype = TableExprFuncNode::nfalseFUNC; } else if (funcName == "nfalses") { ftype = TableExprFuncNode::nfalsesFUNC; } else if (funcName == "array") { ftype = TableExprFuncNode::arrayFUNC; } else if (funcName == "transpose") { ftype = TableExprFuncNode::transposeFUNC; } 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 == "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 == "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::gvarianceFUNC; } else if (funcName == "gvariances") { ftype = TableExprFuncNode::gvariancesFUNC; } else if (funcName == "gstddev") { ftype = TableExprFuncNode::gstddevFUNC; } else if (funcName == "gstddevs") { ftype = TableExprFuncNode::gstddevsFUNC; } 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.nelements()); if (found || (!ignoreFuncs.empty() && ftype >= TableExprFuncNode::FirstAggrFunc)) { throw (TableInvExpr ("Function '" + funcName + "' can only be used in TaQL")); } return ftype; } //# Parse the name of a function. TableExprNode TableParseSelect::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. // This can only happen in the PCALC case. if (fromTables_p.size() == 0) { if (commandType_p != PCALC) { throw TableInvExpr("No table given"); } return makeFuncNode (this, name, arguments, ignoreFuncs, Table(), style); } TableExprNode node = makeFuncNode (this, name, arguments, ignoreFuncs, fromTables_p[0].table(), style); // A rowid function node needs to be added to applySelNodes_p. const TableExprNodeRep* rep = node.getNodeRep(); if (dynamic_cast(rep)) { addApplySelNode (node); } return node; } TableExprNode TableParseSelect::makeUDFNode (TableParseSelect* sel, const String& name, const TableExprNodeSet& arguments, const Table& table, const TaQLStyle& style) { TableExprNode udf; if (sel) { Vector parts = stringToVector (name, '.'); if (parts.size() > 2) { // At least 3 parts; see if the first part is a table shorthand. Table tab = sel->findTable (parts[0]); if (! tab.isNull()) { udf = TableExprNode::newUDFNode (name.substr(parts[0].size() + 1), arguments, tab, style); } } } // If not created, use the full name and given (i.e. first) table. if (udf.isNull()) { udf = TableExprNode::newUDFNode (name, arguments, table, style); } // A UDF might create table column nodes, so add it to applySelNodes_p. if (sel) { sel->addApplySelNode (udf); } return udf; } //# Parse the name of a function. TableExprNode TableParseSelect::makeFuncNode (TableParseSelect* sel, const String& fname, const TableExprNodeSet& arguments, const Vector& ignoreFuncs, const Table& tabin, const TaQLStyle& style) { Table table(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, '.'); if (sel && parts.size() == 2) { // See if xx is a shorthand. If so, use that table. Table tab = sel->findTable (parts[0]); if (! tab.isNull()) { table = tab; name = parts[1]; } } //# Determine the function type. TableExprFuncNode::FunctionType ftype = findFunc (name, arguments.nelements(), ignoreFuncs); if (ftype == TableExprFuncNode::NRFUNC) { // The function can be a user defined one (or unknown). return makeUDFNode (sel, name, arguments, table, style); } try { // The axes of reduction functions like 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: axarg = 2; // fall through!! case TableExprFuncNode::arrsumsFUNC: case TableExprFuncNode::arrproductsFUNC: case TableExprFuncNode::arrsumsqrsFUNC: case TableExprFuncNode::arrminsFUNC: case TableExprFuncNode::arrmaxsFUNC: case TableExprFuncNode::arrmeansFUNC: case TableExprFuncNode::arrvariancesFUNC: case TableExprFuncNode::arrstddevsFUNC: case TableExprFuncNode::arravdevsFUNC: case TableExprFuncNode::arrrmssFUNC: case TableExprFuncNode::arrmediansFUNC: case TableExprFuncNode::anysFUNC: case TableExprFuncNode::allsFUNC: case TableExprFuncNode::ntruesFUNC: case TableExprFuncNode::nfalsesFUNC: case TableExprFuncNode::runminFUNC: case TableExprFuncNode::runmaxFUNC: case TableExprFuncNode::runmeanFUNC: case TableExprFuncNode::runvarianceFUNC: case TableExprFuncNode::runstddevFUNC: case TableExprFuncNode::runavdevFUNC: case TableExprFuncNode::runrmsFUNC: case TableExprFuncNode::runmedianFUNC: case TableExprFuncNode::runanyFUNC: case TableExprFuncNode::runallFUNC: case TableExprFuncNode::boxminFUNC: case TableExprFuncNode::boxmaxFUNC: case TableExprFuncNode::boxmeanFUNC: case TableExprFuncNode::boxvarianceFUNC: case TableExprFuncNode::boxstddevFUNC: case TableExprFuncNode::boxavdevFUNC: case TableExprFuncNode::boxrmsFUNC: case TableExprFuncNode::boxmedianFUNC: case TableExprFuncNode::boxanyFUNC: case TableExprFuncNode::boxallFUNC: case TableExprFuncNode::arrayFUNC: case TableExprFuncNode::transposeFUNC: case TableExprFuncNode::diagonalFUNC: if (arguments.nelements() >= axarg) { TableExprNodeSet parms; // Add first argument(s) to the parms. for (uInt i=0; i()))); parms.add (arg); } } else if (arguments.nelements() == axarg+1 && arguments[axarg].isSingle()) { // A single set given; see if it is an array. const TableExprNodeSetElem& 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; ivalueType() != 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, table, 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, table, 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); } } //# 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 TableParseSelect::handleColumn (Int stringType, const String& name, const TableExprNode& expr, const String& newName, const String& newNameMask, const String& newDtype) { if (expr.isNull() && stringType >= 0) { // A wildcarded column name is given. handleWildColumn (stringType, name); } else { // A single column is given. Int nrcol = columnNames_p.nelements(); 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); if (columnExpr_p[nrcol].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(j); str = str.after(j); } } regex = Regex::fromPattern (str); } else { if (!negate) { int j = str.index("\\."); if (j >= 0) { shorthand = str.before(j); str = str.after(j+1); } } if (name[0] == 'f') { regex = Regex(str); } else { regex = Regex(".*(" + str + ").*"); } } if (!negate) { // Add back the delimiting . if a shorthand is given. if (! shorthand.empty()) { shorthand += '.'; } // Find all matching columns. Table tab = findTable(String()); Vector columns = tab.tableDesc().columnNames(); 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. void TableParseSelect::handleColumnFinish (Bool distinct) { distinct_p = distinct; // Remove the deleted column names. // Create Expr objects for the wildcarded names. Int nrcol = columnNames_p.size(); if (nrcol > 0) { if (resultSet_p != 0) { 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); names[nr] = name; oldNames[nr] = name; // Get the keywords for this column (to copy unit, etc.) TableColumn tabcol(exprs[nr].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_p && columnNames_p.nelements() == 0) { 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) { makeProjectExprTable(); } } Table TableParseSelect::createTable (const TableDesc& td, Int64 nrow, const Record& dmInfo) { // 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; } void TableParseSelect::makeProjectExprTable() { // 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 ivec(spec.toArrayInt(i)); if (isCOrder) { Int nd = ivec.nelements(); shape.resize (nd); for (Int i=0; i& nodes, Bool rollup) { groupbyNodes_p = nodes; groupbyRollup_p = rollup; if (rollup) { throw TableInvExpr ("ROLLUP is not supported yet in the GROUPBY"); } for (uInt i=0; i 0, AipsError); table_p = fromTables_p[0].table(); table_p.reopenRW(); if (! table_p.isWritable()) { throw TableInvExpr ("Table " + table_p.tableName() + " is not writable"); } } void TableParseSelect::handleAddCol (const Record& dmInfo) { if (dmInfo.empty()) { StandardStMan ssm; table_p.addColumn (tableDesc_p, ssm); } else { table_p.addColumn (tableDesc_p, dmInfo); } } ValueHolder TableParseSelect::getRecFld (const String& name) { String keyName; TableRecord& keyset = findKeyword (name, keyName); Int fieldnr = keyset.fieldNumber (keyName); if (fieldnr < 0) { throw (TableInvExpr ("Keyword " + name + " does not exist")); } return keyset.asValueHolder (fieldnr); } void TableParseSelect::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 TableParseSelect::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; } TableRecord& TableParseSelect::findKeyword (const String& name, String& keyName) { //# Split the name into optional shorthand, column, and keyword. String shand, columnName; Vector fieldNames; splitName (shand, columnName, fieldNames, name, True, True, False); Table tab = findTable (shand); if (tab.isNull()) { throw (TableInvExpr("Shorthand " + shand + " has not been defined")); } TableRecord* rec; String fullName; if (columnName.empty()) { rec = TableExprNode::findLastKeyRec (tab.rwKeywordSet(), fieldNames, fullName); } else { TableRecord& colkeys (TableColumn(tab, columnName).rwKeywordSet()); rec = TableExprNode::findLastKeyRec (colkeys, fieldNames, fullName); } keyName = fieldNames[fieldNames.size() -1 ]; return *rec; } void TableParseSelect::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 { setRecFld (keyset, keyName, dtype, value); } } void TableParseSelect::handleRenameKey (const String& oldName, const String& newName) { String keyName; TableRecord& keyset = findKeyword (oldName, keyName); keyset.renameField (newName, keyName); } void TableParseSelect::handleRemoveKey (const String& name) { String keyName; TableRecord& keyset = findKeyword (name, keyName); keyset.removeField (keyName); } void TableParseSelect::handleWhere (const TableExprNode& node) { checkAggrFuncs (node); node_p = node; } void TableParseSelect::handleSort (const std::vector& sort, Bool noDuplicates, Sort::Order order) { noDupl_p = noDuplicates; order_p = order; sort_p = sort; } void TableParseSelect::handleCalcComm (const TableExprNode& node) { checkAggrFuncs (node); node_p = node; } Block TableParseSelect::getStoredColumns (const Table& tab) const { Block names; const TableDesc& tdesc = tab.tableDesc(); for (uInt i=0; i(tabcol).getColumn()); break; case TpUChar: tsnptr = new TableExprNodeArrayConstInt (ScalarColumn(tabcol).getColumn()); break; case TpShort: tsnptr = new TableExprNodeArrayConstInt (ScalarColumn(tabcol).getColumn()); break; case TpUShort: tsnptr = new TableExprNodeArrayConstInt (ScalarColumn(tabcol).getColumn()); break; case TpInt: tsnptr = new TableExprNodeArrayConstInt (ScalarColumn(tabcol).getColumn()); break; case TpUInt: tsnptr = new TableExprNodeArrayConstInt (ScalarColumn(tabcol).getColumn()); break; case TpFloat: tsnptr = new TableExprNodeArrayConstDouble (ScalarColumn(tabcol).getColumn()); break; case TpDouble: tsnptr = new TableExprNodeArrayConstDouble (ScalarColumn(tabcol).getColumn()); break; case TpComplex: tsnptr = new TableExprNodeArrayConstDComplex (ScalarColumn(tabcol).getColumn()); break; case TpDComplex: tsnptr = new TableExprNodeArrayConstDComplex (ScalarColumn(tabcol).getColumn()); break; case TpString: tsnptr = new TableExprNodeArrayConstString (ScalarColumn(tabcol).getColumn()); break; default: throw (TableInvExpr ("Nested query column " + colDesc.name() + " has unknown data type")); } } else { switch (colDesc.dataType()) { case TpBool: tsnptr = new TableExprNodeArrayConstBool (ArrayColumn(tabcol).getColumn()); break; case TpUChar: tsnptr = new TableExprNodeArrayConstInt (ArrayColumn(tabcol).getColumn()); break; case TpShort: tsnptr = new TableExprNodeArrayConstInt (ArrayColumn(tabcol).getColumn()); break; case TpUShort: tsnptr = new TableExprNodeArrayConstInt (ArrayColumn(tabcol).getColumn()); break; case TpInt: tsnptr = new TableExprNodeArrayConstInt (ArrayColumn(tabcol).getColumn()); break; case TpUInt: tsnptr = new TableExprNodeArrayConstInt (ArrayColumn(tabcol).getColumn()); break; case TpFloat: tsnptr = new TableExprNodeArrayConstDouble (ArrayColumn(tabcol).getColumn()); break; case TpDouble: tsnptr = new TableExprNodeArrayConstDouble (ArrayColumn(tabcol).getColumn()); break; case TpComplex: tsnptr = new TableExprNodeArrayConstDComplex (ArrayColumn(tabcol).getColumn()); break; case TpDComplex: tsnptr = new TableExprNodeArrayConstDComplex (ArrayColumn(tabcol).getColumn()); break; case TpString: tsnptr = new TableExprNodeArrayConstString (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; } TableExprNode TableParseSelect::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. resultSet_p->link(); TableExprNodeSet set(rownrs_p, *resultSet_p); return set.setOrArray(); } void TableParseSelect::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 TableParseSelect::handleLimit (const TableExprNode& expr) { limit_p = evalIntScaExpr (expr); } void TableParseSelect::handleOffset (const TableExprNode& expr) { offset_p = evalIntScaExpr (expr); } void TableParseSelect::makeTableNoFrom (const vector& stack) { 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. Table tab(Table::Memory); tab.addRow(nrow); addTable (-1, String(), tab, String(), std::vector(), stack); } void TableParseSelect::handleAddRow (const TableExprNode& expr) { table_p.addRow (evalIntScaExpr (expr)); } Int64 TableParseSelect::evalIntScaExpr (const TableExprNode& expr) const { checkAggrFuncs (expr); if (!expr.table().isNull()) { 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 TableParseSelect::handleUpdate() { columnNames_p.resize (update_p.size()); for (uInt i=0; icolumnName(); } } void TableParseSelect::handleInsert() { // If no columns were given, all stored columns in the first table // are the target columns. if (columnNames_p.nelements() == 0) { columnNames_p = getStoredColumns (fromTables_p[0].table()); columnNameMasks_p.resize (columnNames_p.size()); } // Check if #columns and values match. // Copy the names to the update objects. if (update_p.size() != columnNames_p.nelements()) { throw TableInvExpr ("Error in INSERT command; nr of columns (=" + String::toString(columnNames_p.nelements()) + ") mismatches " "number of VALUES expressions (=" + String::toString(Int(update_p.size())) + ")"); } for (uInt i=0; isetColumnName (columnNames_p[i]); update_p[i]->setColumnNameMask (columnNameMasks_p[i]); } } void TableParseSelect::handleInsert (TableParseSelect* sel) { insSel_p = sel; } void TableParseSelect::handleCount() { if (columnExpr_p.size() == 0) { throw TableInvExpr ("No COUNT columns given"); } for (uInt i=0; i& rownrs, const CountedPtr& 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. const TableDesc& tabdesc = updTable.tableDesc(); uInt nrkey = update_p.size(); Block cols(nrkey); Block > maskCols(nrkey); Block dtypeCol(nrkey); Block isScalarCol(nrkey); for (uInt i=0; iisSingle()) { isScalar = True; } } //# Check if the value type matches. if (isScalar && !key.node().isScalar()) { throw TableInvExpr ("An array value cannot be used in UPDATE of " " scalar element of column " + colName + " in table " + updTable.tableName()); } cols[i].attach (updTable, colName); dtypeCol[i] = coldesc.dataType(); // If needed, make the expression's unit the same as the column unit. key.node().adaptUnit (TableExprNodeColumn::getColumnUnit (TableColumn(updTable, colName))); } // Loop through all rows in the table and update each row. TableExprIdAggr rowid(groups); for (uInt row=0; rowgetSlicer(rowid)); } Bool isSca = isScalarCol[i]; // Evaluate a possible mask. MArray mask; if (! key.mask().isNull()) { key.mask().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. switch (node.getNodeRep()->dataType()) { case TableExprNodeRep::NTBool: switch (dtypeCol[i]) { case TpBool: updateValue (row, rowid, isSca, node, mask.array(), key.maskFirst(), col, slicerPtr, maskCols[i]); break; default: throw TableInvExpr ("Column " + update_p[i]->columnName() + " has an invalid data type for an" " UPDATE with a bool value"); } break; case TableExprNodeRep::NTString: switch (dtypeCol[i]) { case TpString: updateValue (row, rowid, isSca, node, mask.array(), key.maskFirst(), col, slicerPtr, maskCols[i]); break; default: throw TableInvExpr ("Column " + update_p[i]->columnName() + " has an invalid data type for an" " UPDATE with a string value"); } break; case TableExprNodeRep::NTInt: switch (dtypeCol[i]) { case TpUChar: updateValue (row, rowid, isSca, node, mask.array(), key.maskFirst(), col, slicerPtr, maskCols[i]); break; case TpShort: updateValue (row, rowid, isSca, node, mask.array(), key.maskFirst(), col, slicerPtr, maskCols[i]); break; case TpUShort: updateValue (row, rowid, isSca, node, mask.array(), key.maskFirst(), col, slicerPtr, maskCols[i]); break; case TpInt: updateValue (row, rowid, isSca, node, mask.array(), key.maskFirst(), col, slicerPtr, maskCols[i]); break; case TpUInt: updateValue (row, rowid, isSca, node, mask.array(), key.maskFirst(), col, slicerPtr, maskCols[i]); break; case TpFloat: updateValue (row, rowid, isSca, node, mask.array(), key.maskFirst(), col, slicerPtr, maskCols[i]); break; case TpDouble: updateValue (row, rowid, isSca, node, mask.array(), key.maskFirst(), col, slicerPtr, maskCols[i]); break; case TpComplex: updateValue (row, rowid, isSca, node, mask.array(), key.maskFirst(), col, slicerPtr, maskCols[i]); break; case TpDComplex: updateValue (row, rowid, isSca, node, mask.array(), key.maskFirst(), col, slicerPtr, maskCols[i]); break; default: throw TableInvExpr ("Column " + update_p[i]->columnName() + " has an invalid data type for an" " UPDATE with an integer value"); } break; case TableExprNodeRep::NTDouble: case TableExprNodeRep::NTDate: switch (dtypeCol[i]) { case TpUChar: updateValue (row, rowid, isSca, node, mask.array(), key.maskFirst(), col, slicerPtr, maskCols[i]); break; case TpShort: updateValue (row, rowid, isSca, node, mask.array(), key.maskFirst(), col, slicerPtr, maskCols[i]); break; case TpUShort: updateValue (row, rowid, isSca, node, mask.array(), key.maskFirst(), col, slicerPtr, maskCols[i]); break; case TpInt: updateValue (row, rowid, isSca, node, mask.array(), key.maskFirst(), col, slicerPtr, maskCols[i]); break; case TpUInt: updateValue (row, rowid, isSca, node, mask.array(), key.maskFirst(), col, slicerPtr, maskCols[i]); break; case TpFloat: updateValue (row, rowid, isSca, node, mask.array(), key.maskFirst(), col, slicerPtr, maskCols[i]); break; case TpDouble: updateValue (row, rowid, isSca, node, mask.array(), key.maskFirst(), col, slicerPtr, maskCols[i]); break; case TpComplex: updateValue (row, rowid, isSca, node, mask.array(), key.maskFirst(), col, slicerPtr, maskCols[i]); break; case TpDComplex: updateValue (row, rowid, isSca, node, mask.array(), key.maskFirst(), col, slicerPtr, maskCols[i]); break; default: throw TableInvExpr ("Column " + update_p[i]->columnName() + " has an invalid data type for an" " UPDATE with a double value"); } break; case TableExprNodeRep::NTComplex: switch (dtypeCol[i]) { case TpComplex: updateValue (row, rowid, isSca, node, mask.array(), key.maskFirst(), col, slicerPtr, maskCols[i]); break; case TpDComplex: updateValue (row, rowid, isSca, node, mask.array(), key.maskFirst(), col, slicerPtr, maskCols[i]); break; default: throw TableInvExpr ("Column " + update_p[i]->columnName() + " has an invalid data type for an" " UPDATE with a complex value"); } break; default: throw TableInvExpr ("Unknown UPDATE expression data type"); } } } if (showTimings) { timer.show (" Update "); } } template void TableParseSelect::copyMaskedValue (uInt row, ArrayColumn& acol, const Slicer* slicerPtr, const TNODE* val, uInt 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); } } Array TableParseSelect::makeMaskSlice (const Array& mask, Bool maskFirst, const IPosition& shapeCol, const Slicer* slicerPtr) { if (! slicerPtr || maskFirst) { if (! mask.shape().isEqual (shapeCol)) { throw AipsError ("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) { // Mask before section, so apply the section to the mask. return mask(*slicerPtr); } else { if (! mask.shape().isEqual (length)) { throw AipsError ("Update mask must conform the column's array section"); } } } return mask; } template void TableParseSelect::updateScalar (uInt 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 TableParseSelect::updateArray (uInt 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 TableParseSelect::updateSlice (uInt 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); } } } void TableParseSelect::checkMaskColumn (Bool hasMask, const ArrayColumn& maskCol, const TableColumn& col) { if (! maskCol.isNull()) { /// if (maskCol.isNull()) { /// if (hasMask) { /// throw AipsError ("An update mask column must be given for a " /// "masked array expression in update of column " + /// col.columnDesc().name()); /// } /// } else { if (! hasMask) { throw AipsError ("No update mask column can be given for an " "unmasked expression in update of column " + col.columnDesc().name()); } } } template void TableParseSelect::updateValue (uInt row, const TableExprId& rowid, Bool isScalarCol, const TableExprNode& node, const Array& mask, Bool maskFirst, 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, maskFirst, shapeCol, slicerPtr)); // Get the expression data (scalar or array). TNODE sval; const TNODE* ptr = &sval; size_t incr = 0; Bool deleteIt; 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); } } } } } } //# Execute the inserts. Table TableParseSelect::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 (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; inode() = 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 (columnNames_p.nelements() == 0) { columnNames_p = getStoredColumns (table); } // Get the source columns. Block sourceNames; sourceNames = insSel_p->getColumnNames(); if (sourceNames.nelements() == 0) { sourceNames = getStoredColumns (sel); } // Check if the number of columns match. if (sourceNames.nelements() != columnNames_p.nelements()) { throw TableInvExpr ("Error in INSERT command; nr of columns (=" + String::toString(columnNames_p.nelements()) + ") mismatches " "number of columns in selection (=" + String::toString(sourceNames.nelements()) + ")"); } // 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(columnNames_p)); ROTableRow rowfrom (sel, Vector(sourceNames)); for (uInt 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. uInt 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. CountedPtr TableParseSelect::doGroupby (Bool showTimings, vector aggrNodes, Int groupAggrUsed) { Timer timer; // If only 'select count(*)' was given, get the size of the WHERE, // thus the size of rownrs_p. CountedPtr result; if ((groupAggrUsed & ONLY_COUNTALL) != 0 && (groupAggrUsed & GROUPBY) == 0) { result = doOnlyCountAll (aggrNodes[0]); } else { result = doGroupByAggr (aggrNodes); } if (showTimings) { timer.show (" Groupby "); } return result; } Table TableParseSelect::adjustApplySelNodes (const Table& table) { for (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; } void TableParseSelect::doHaving (Bool showTimings, const CountedPtr& groups) { Timer timer; // Find the rows matching the HAVING expression. Vector rownrs(rownrs_p.size()); uInt nr = 0; TableExprIdAggr rowid(groups); for (uInt i=0; i TableParseSelect::doOnlyCountAll (TableExprNodeRep* aggrNode) { // 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. vector > funcSets (1, new TableExprGroupFuncSet()); CountedPtr funcb = aggrNode->makeGroupAggrFunc(); TableExprGroupCountAll& func = dynamic_cast(*funcb); // Note: add turns it into a CountedPtr, so it will be deleted automatically. funcSets[0]->add (funcb); // The nr of rows is the result of count(*), so simply set it. func.setResult (rownrs_p.size()); // The resulting table has only 1 group; use the last row with it. if (! rownrs_p.empty()) { rownrs_p.reference (Vector(1, rownrs_p[rownrs_p.size()-1])); } // Save the aggregation results in a result object. return CountedPtr(new TableExprGroupResult(funcSets)); } vector > TableParseSelect::doGroupByAggrMultipleKeys (const vector& aggrNodes) { // We have to group the data according to the (maybe 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. vector > funcSets; std::map keyFuncMap; // Create the set of groupby key objects. TableExprGroupKeySet keySet(groupbyNodes_p); // Loop through all rows. // For each row generate the key to get the right entry. TableExprId rowid(0); for (uInt i=0; i::iterator iter=keyFuncMap.find (keySet); if (iter == keyFuncMap.end()) { keyFuncMap[keySet] = groupnr; funcSets.push_back (new TableExprGroupFuncSet (aggrNodes)); } else { groupnr = iter->second; } funcSets[groupnr]->apply (rowid); } return funcSets; } CountedPtr TableParseSelect::doGroupByAggr (const vector& aggrNodes) { // Get the aggregate functions to be evaluated lazily. vector immediateNodes; vector lazyNodes; for (uInt i=0; imakeGroupAggrFunc(); if (aggrNodes[i]->isLazyAggregate()) { lazyNodes.push_back (aggrNodes[i]); } else { immediateNodes.push_back (aggrNodes[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()); if (! lazyNodes.empty()) { immediateNodes.push_back (&expridNode); } vector > funcSets; // Use a faster way for a single groupby key. if (groupbyNodes_p.size() == 1 && groupbyNodes_p[0].dataType() == TpDouble) { funcSets = doGroupByAggrSingleKey (immediateNodes); } else if (groupbyNodes_p.size() == 1 && groupbyNodes_p[0].dataType() == TpInt) { funcSets = doGroupByAggrSingleKey (immediateNodes); } else { funcSets = doGroupByAggrMultipleKeys (immediateNodes); } // 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 rownrs(funcSets.size()); vector > > ids; ids.reserve (funcSets.size()); uInt n=0; for (uInt i=0; i >& funcs = funcSets[i]->getFuncs(); for (uInt j=0; jfinish(); } rownrs[n++] = funcSets[i]->getId().rownr(); if (! lazyNodes.empty()) { ids.push_back (funcSets[i]->getFuncs()[nimmediate]->getIds()); } } rownrs_p.reference (rownrs); // Save the aggregation results in a result object. CountedPtr result (new TableExprGroupResult (funcSets, ids)); return result; } void replaceIds (vector > >& ids) { // Combine all rowids in a single vector, so it can be sorted. Int64 nrow = 0; for (size_t i=0; isize(); } Vector rowids(nrow); Int64 inx = 0; for (size_t i=0; i& vec = *ids[i]; for (size_t j=0; j inxVec; GenSortIndirect::sort (inxVec, rowids); // We need to replace each rowid by its sequence nr because a table selection // will map the selected rows to rowid 0..n. // So store the index in the rowids. for (uInt i=0; i& vec = *ids[i]; for (size_t j=0; j arrays(nrkey); Sort sort; Bool deleteIt; for (i=0; i* array = new Array (key.node().getColumnBool(rownrs_p)); arrays[i] = array; const Bool* data = array->getStorage (deleteIt); sort.sortKey (data, TpBool, 0, getOrder(key)); array->freeStorage (data, deleteIt); } break; case TpUChar: { Array* array = new Array (key.node().getColumnuChar(rownrs_p)); arrays[i] = array; const uChar* data = array->getStorage (deleteIt); sort.sortKey (data, TpUChar, 0, getOrder(key)); array->freeStorage (data, deleteIt); } break; case TpShort: { Array* array = new Array (key.node().getColumnShort(rownrs_p)); arrays[i] = array; const Short* data = array->getStorage (deleteIt); sort.sortKey (data, TpShort, 0, getOrder(key)); array->freeStorage (data, deleteIt); } break; case TpUShort: { Array* array = new Array (key.node().getColumnuShort(rownrs_p)); arrays[i] = array; const uShort* data = array->getStorage (deleteIt); sort.sortKey (data, TpUShort, 0, getOrder(key)); array->freeStorage (data, deleteIt); } break; case TpInt: { Array* array = new Array (key.node().getColumnInt(rownrs_p)); arrays[i] = array; const Int* data = array->getStorage (deleteIt); sort.sortKey (data, TpInt, 0, getOrder(key)); array->freeStorage (data, deleteIt); } break; case TpUInt: { Array* array = new Array (key.node().getColumnuInt(rownrs_p)); arrays[i] = array; const uInt* data = array->getStorage (deleteIt); sort.sortKey (data, TpUInt, 0, getOrder(key)); array->freeStorage (data, deleteIt); } break; case TpFloat: { Array* array = new Array (key.node().getColumnFloat(rownrs_p)); arrays[i] = array; const Float* data = array->getStorage (deleteIt); sort.sortKey (data, TpFloat, 0, getOrder(key)); array->freeStorage (data, deleteIt); } break; case TpDouble: { Array* array = new Array (key.node().getColumnDouble(rownrs_p)); arrays[i] = array; const Double* data = array->getStorage (deleteIt); sort.sortKey (data, TpDouble, 0, getOrder(key)); array->freeStorage (data, deleteIt); } break; case TpComplex: { Array* array = new Array (key.node().getColumnComplex(rownrs_p)); arrays[i] = array; const Complex* data = array->getStorage (deleteIt); sort.sortKey (data, TpComplex, 0, getOrder(key)); array->freeStorage (data, deleteIt); } break; case TpDComplex: { Array* array = new Array (key.node().getColumnDComplex(rownrs_p)); arrays[i] = array; const DComplex* data = array->getStorage (deleteIt); sort.sortKey (data, TpDComplex, 0, getOrder(key)); array->freeStorage (data, deleteIt); } break; case TpString: { Array* array = new Array (key.node().getColumnString(rownrs_p)); arrays[i] = array; const String* data = array->getStorage (deleteIt); sort.sortKey (data, TpString, 0, getOrder(key)); array->freeStorage (data, deleteIt); } break; default: AlwaysAssert (False, AipsError); } } uInt nrrow = rownrs_p.size(); Vector newRownrs (nrrow); int sortOpt = Sort::HeapSort; if (noDupl_p) { sortOpt += Sort::NoDuplicates; } sort.sort (newRownrs, nrrow, sortOpt); for (i=0; i*)arrays[i]; break; case TpUChar: delete (Array*)arrays[i]; break; case TpShort: delete (Array*)arrays[i]; break; case TpUShort: delete (Array*)arrays[i]; break; case TpInt: delete (Array*)arrays[i]; break; case TpUInt: delete (Array*)arrays[i]; break; case TpFloat: delete (Array*)arrays[i]; break; case TpDouble: delete (Array*)arrays[i]; break; case TpComplex: delete (Array*)arrays[i]; break; case TpDComplex: delete (Array*)arrays[i]; break; case TpString: delete (Array*)arrays[i]; break; default: AlwaysAssert (False, AipsError); } } if (showTimings) { timer.show (" Orderby "); } // Convert index to rownr. for (uInt 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 TableParseSelect::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 TableParseSelect::doProject (Bool showTimings, const Table& table, const CountedPtr& groups) { Timer timer; Table tabp; if (nrSelExprUsed_p > 0) { // 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 = tabp.project (columnOldNames_p); for (uInt i=0; i& 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. for (uInt i=0; i 0) { table.deepCopy (resultName_p, dminfo_p, storageOption_p, overwrite_p ? Table::New : Table::NewNoReplace, True, endianFormat_p); result = Table(resultName_p); } else { // Normal reference table. table.rename (resultName_p, overwrite_p ? Table::New : Table::NewNoReplace); table.flush(); } } if (showTimings) { timer.show (" Giving "); } return result; } DataType TableParseSelect::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 == "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"); } return dtype; } void TableParseSelect::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 String& unitName) { 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 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.nelements() > 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 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); // If no keys defined for this column, define Epoch measure for dates. 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. String unit(unitName); if (dtype == TpQuantity && unit.empty()) { unit = "d"; } if (! unit.empty()) { cd.rwKeywordSet().define ("QuantumUnits", Vector(1, unit)); } } Table TableParseSelect::doDistinct (Bool showTimings, const Table& table) { Timer timer; Table result; // Sort the table uniquely on all columns. Table tabs = table.sort (columnNames_p, 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 TableParseSelect::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 TableParseSelect::handleGiving (const TableExprNodeSet& set) { // In principle GIVING is handled before SELECT, so below is always false. // But who knows what future brings us. if (! columnNames_p.empty()) { throw TableInvExpr("Expressions can be given in SELECT or GIVING, " "not both"); } TableExprNodeRep::checkAggrFuncs (&set); resultSet_p = new TableExprNodeSet (set); } void TableParseSelect::checkTableProjSizes() const { // Check if all tables used in non-constant select expressions // have the same size as the first table. Int64 nrow = fromTables_p[0].table().nrow(); for (uInt i=0; iisConstant()) { if (columnExpr_p[i].getNodeRep()->nrow() != nrow) { throw TableInvExpr("Nr of rows of tables used in select " "expressions must be equal to first table"); } } } } //# Execute all parts of a TaQL command doing some selection. void TableParseSelect::execute (Bool showTimings, Bool setInGiving, Bool mustSelect, uInt maxRow, Bool doTracing) { //# 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 && columnNames_p.nelements() == 0 && 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. makeProjectExprSel(); //# Get nodes representing aggregate functions. //# Test if aggregate, groupby, or having is used. vector aggrNodes; Int groupAggrUsed = testGroupAggr (aggrNodes); if (groupAggrUsed == 0) { // Check if tables used in projection have the same size. checkTableProjSizes(); } else if (doTracing) { cerr << "GROUPBY to be done using " << aggrNodes.size() << " aggregate nodes" << endl; } // Column nodes used in aggregate functions should not adhere applySelection. uInt ndis = 0; for (uInt i=0; i colNodes; aggrNodes[i]->getColumnNodes (colNodes); for (uInt j=0; jdisableApplySelection(); ndis++; } } if (doTracing) { cerr << " disableApplySelection done in " << ndis << " column nodes of aggregate nodes" << endl; } // Select distinct makes no sense if aggregate and no groupby is given. if (groupAggrUsed != 0 && (groupAggrUsed & GROUPBY) == 0) { distinct_p = False; } //# The first table in the list is the source table. Table table = fromTables_p[0].table(); //# 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. uInt nrmax=0; if (endrow_p > 0 && sort_p.size() == 0 && !distinct_p && groupAggrUsed == 0) { 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 (groupAggrUsed != 0) { groupResult = doGroupby (showTimings, aggrNodes, groupAggrUsed); // 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 (! projectExprSubset_p.empty()) { doProjectExpr (True, groupResult); resultTable = adjustApplySelNodes(table); table = resultTable; if (doTracing) { cerr << "Pre-projected " << projectExprSubset_p.size() << " columns" << endl; cerr << " applySelection called for " << applySelNodes_p.size() << " nodes" << endl; } } // Do the possible HAVING step. if (! havingNode_p.isNull()) { 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, dminfo_p, storageOption_p, overwrite_p ? Table::New : Table::NewNoReplace, True, endianFormat_p); projectExprTable_p = Table(resultName_p); Table::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 (columnNames_p.nelements() > 0) { resultTable = doProject (showTimings, table, groupResult); if (doTracing) { cerr << "Final projection done of " << columnNames_p.size() - projectExprSubset_p.size() << " 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); if (doTracing) { cerr << "Finished the GIVING command" << endl; } } } //# Keep the table for later. table_p = resultTable; } void TableParseSelect::checkAggrFuncs (const TableExprNode& node) { if (! node.isNull()) { TableExprNodeRep::checkAggrFuncs (node.getNodeRep()); } } //# Get aggregate functions used and check if used at correct places. //# Also check that HAVING is not solely used. Int TableParseSelect::testGroupAggr (vector& aggr) const { // Make sure main (where) node does not have aggregate functions. // This has been checked before, but use defensive programming. if (! node_p.isNull()) { const_cast(node_p.getNodeRep())->getAggrNodes (aggr); AlwaysAssert (aggr.empty(), AipsError); } // Get possible aggregate functions used in SELECT and HAVING. for (uInt i=0; i(columnExpr_p[i].getNodeRep())->getAggrNodes (aggr); } uInt nselAggr = aggr.size(); if (! havingNode_p.isNull()) { const_cast(havingNode_p.getNodeRep())->getAggrNodes (aggr); } // Make sure aggregate functions are not used in a UPDATE command, etc. // Again, this cannot happen but use defensive programming. if (commandType_p != PSELECT) { AlwaysAssert (aggr.empty(), AipsError); return 0; } // Make sure HAVING is only used if SELECT has an aggregate function // or if GROUPBY is used. if (! havingNode_p.isNull()) { if (nselAggr == 0 && groupbyNodes_p.empty()) { throw TableInvExpr ("HAVING can only be used if GROUPBY is used or " "an aggregate function is used in SELECT"); } } // Test if any group/aggr is given or if only // 'SELECT COUNT(*)' is given without GROUPBY. Int res = 0; if (! groupbyNodes_p.empty()) res += GROUPBY; if (! aggr.empty()) res += AGGR_FUNCS; if (nselAggr == 1 && aggr.size() == 1) { TableExprAggrNode* node = dynamic_cast(aggr[0]); if (node && node->funcType() == TableExprFuncNode::countallFUNC) { res += ONLY_COUNTALL; } } return res; } void TableParseSelect::show (ostream& os) const { if (! node_p.isNull()) { node_p.show (os); } } //# 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. if (hrval.getNames()) { Vector tmp(*(hrval.getNames())); cols.reference (tmp); } else { cols.resize (0); } return hrval.getTable(); } catch (std::exception& x) { throw TableParseError ("'" + str + "'\n " + x.what()); } } } //# NAMESPACE CASACORE - END casacore-2.4.1/tables/TaQL/TableParse.h000066400000000000000000001036311321422335000175430ustar00rootroot00000000000000//# TableParse.h: Classes to hold results from table 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef TABLES_TABLEPARSE_H #define TABLES_TABLEPARSE_H //# Includes #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 TableColumn; class AipsIO; template class Vector; // // Class to hold values from table grammar parser // // // // // //# Classes you should understand before using this one. // // // TableParse is the class used to parse a table command. // // // TableParse is used by the parser of table select statements. // The parser is written in Bison and Flex in files TableGram.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 container. The variable names can be qualified // by the table name and will be looked up in the appropriate table. // // A select command is similar to SQL and can look like: // SELECT columns FROM tab1 sh1, tab2 sh2, tab3 WHERE // sh1.field == 3*sh1.field2 ... ORDERBY columns GIVING table // This is described in more detail in TableGram.l. // // The class TableParse only contains information about a table // used in the table command. // // Global functions are used to operate on the information. // The main function is the global function tableCommand. // It executes the given TaQL command and returns the resulting table. // This is, in fact, the only function to be used by a user. // // // 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 TableParse { public: // Default constructor for container class. TableParse(); // Copy constructor (copy semantics). TableParse (const TableParse&); // Assignment (copy semantics). TableParse& operator= (const TableParse&); // Associate the table and the shorthand. TableParse (const Table& table, const String& shorthand); // Test if shorthand matches. Bool test (const String& shortHand) const; // Get the shorthand. const String& shorthand() const; // Get table object. const Table& table() const; private: String shorthand_p; Table table_p; }; // // Parse and execute the given command. // It will open (and close) all tables needed. // It returns the resulting table. // The command type (select or update) 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); // // // Helper class for sort keys in TableParse // // // // // //# Classes you should understand before using this one. //
      • TableParse // // // TableParseSort holds a sort expression and order. // // // A table command is parsed. // An object of this class is used to hold the sort expression // and sort order. // class TableParseSort { public: // Construct from a given expression. // The order is not given. TableParseSort(); // Construct from a given expression. // The order is not given. explicit TableParseSort (const TableExprNode&); // Construct from a given expression and for the given order. TableParseSort (const TableExprNode&, Sort::Order); ~TableParseSort(); // Get the expression node. const TableExprNode& node() const; // Get the sort order. Sort::Order order() const; // Is the order given? Bool orderGiven() 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; }; // // Helper class for updates in TableParse // // // // // //# Classes you should understand before using this one. //
      • TableParse // // // TableParseUpdate holds a column name, optional indices, optional mask, // and an update expression. // // // A table command is parsed. // An object of this class is used to hold the column name, optional indices, // and value expression for the UPDATE command. // 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 was not already used. void handleIndices (const TableExprNodeSet& indices, const TaQLStyle& style); ~TableParseUpdate(); // Set the column name. void setColumnName (const String& name); // Set the column name forthe mask. void setColumnNameMask (const String& name); // Get the column name. const String& columnName() const; // Get the possible column name for the mask. const String& columnNameMask() const; // Tell if the mask is given first (i.e., before slice). Bool maskFirst() const { return maskFirst_p; } // Get the pointer to the indices. TableExprNodeIndex* indexPtr() const; // Get the index expression node. const TableExprNode& indexNode() const; // Get the expression node. // const TableExprNode& node() const; TableExprNode& node(); // // Get the mask. const TableExprNode& mask() const { return mask_p; } // Adapt the possible unit of the expression to the possible unit // of the column. void adaptUnit (const Unit& columnUnit); private: String columnName_p; String columnNameMask_p; Bool maskFirst_p; //# True = mask is given before slice TableExprNodeIndex* indexPtr_p; //# copy of pointer in indexNode_p TableExprNode indexNode_p; TableExprNode mask_p; TableExprNode node_p; }; // // Select-class for flex/bison scanner/parser for TableParse // // // // // //# Classes you should understand before using this one. //
      • TableParse //
      • TableGram.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 TableParse objects // as needed and storing them in a vector. // // // 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 TableParseSelect { public: enum CommandType { PSELECT, PUPDATE, PINSERT, PDELETE, PCOUNT, PCALC, PCRETAB, PALTTAB }; enum GroupAggrType { GROUPBY=1, AGGR_FUNCS=2, ONLY_COUNTALL=4 }; // Construct. TableParseSelect (CommandType type); // Destructor. ~TableParseSelect(); // Return the command type. CommandType commandType() const { return commandType_p; } // Return the expression node. TableExprNode getNode() const { return node_p; } // Create a temporary table in no tables are given in FROM. void makeTableNoFrom (const vector& stack); // 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, uInt maxRow, Bool doTracing=False); // 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; // Keep the selection expression. void handleWhere (const TableExprNode&); // Keep the groupby expressions. // It checks if they are all scalar expressions. void handleGroupby (const vector&, Bool rollup); // Keep the having expression. void handleHaving (const TableExprNode&); // Keep the expression of a calculate command. void handleCalcComm (const TableExprNode&); // Keep the create table command. void handleCreTab (const Record& dmInfo); // Keep the column specification in a create table command. void handleColSpec (const String& columnName, 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); // 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); // Define a field with the given data type in the Record. static 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. static String getTypeString (const String& typeStr, DataType type); // 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); // 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. TableRecord& findKeyword (const String& name, String& keyName); // Add an update object. void addUpdate (TableParseUpdate* upd); // Set the insert expressions for all rows. void setInsertExprs (const std::vector exprs) { insertExprs_p = exprs; } // 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 (TableParseSelect* 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 table nr, name, or object to the container. void addTable (Int tabnr, const String& name, const Table& table, const String& shorthand, const vector tempTables, const vector& stack); // 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 makeTable (Int tabnr, const String& name, const Table& ftab, const String& shorthand, const vector tempTables, const vector& stack, Bool alwaysOpen=True); // Replace the first table (used by CALC command). void replaceTable (const Table& table); // 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); // 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&); // Make a function object node for the given function name and arguments. // The ignoreFuncs vector contains invalid function codes. static TableExprNode makeFuncNode (TableParseSelect*, const String& name, const TableExprNodeSet& arguments, const Vector& ignoreFuncs, const Table& table, 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); // Set the DataManager info for a new table. void setDMInfo (const Record& dminfo) { dminfo_p = dminfo;} // 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 projected column names. const Block& getColumnNames() const; // Get the resulting table. const Table& getTable() const; // An exception is thrown if the node uses an aggregate function. static void checkAggrFuncs (const TableExprNode& node); // Split a name into its parts (shorthand, column and field names). // True is returned if the name contained 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 represents a column, fieldNames contains the subfields // of the column (for the case where the column contains records). // If the name is invalid, an exception is thrown if checkError=True. // Otherwise the name is treated as a normal name without keyword. // If allowEmtpy is True, :: is allowed, otherwise an error is thrown. static Bool splitName (String& shorthand, String& columnName, Vector& fieldNames, const String& name, Bool checkError, Bool isKeyword, Bool allowNoKey); private: // Test if groupby or aggregate functions are given. //
        bit 0: on = groupby is given //
        bit 1: on = aggregate functions are given //
        bit 2: on = only select count(*) aggregate function is given Int testGroupAggr (vector& aggr) const; // Get the aggregate functions used in SELECT and HAVING. vector getAggrNodes() const; // Try to make a UDF function node for the given function name and arguments. static TableExprNode makeUDFNode (TableParseSelect*, const String& name, const TableExprNodeSet& arguments, const Table& table, 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); // 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 CountedPtr& groups = CountedPtr()); // 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 CountedPtr& groups = CountedPtr()); // Do the projection containing column expressions. // Use the selected or unselected columns depending on useSel. Table doProjectExpr (Bool useSel, const CountedPtr& groups); // 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); // Make the (empty) table for the epxression in the SELECT clause. void makeProjectExprTable(); // Fill projectExprSelColumn_p telling the columns to be projected // at the first stage. void makeProjectExprSel(); // Add a column node to applySelNodes_p. void addApplySelNode (const TableExprNode& node) { applySelNodes_p.push_back (node); } // 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. CountedPtr doGroupby (bool showTimings, vector aggrNodes, Int groupAggrUsed); // Do the HAVING step. void doHaving (Bool showTimings, const CountedPtr& groups); // Do a groupby/aggregate step that only does a 'select count(*)'. CountedPtr doOnlyCountAll (TableExprNodeRep* aggrNode); // Do a full groupby/aggregate step. CountedPtr doGroupByAggr (const vector& aggrNodes); // 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); // Update the values in the columns (helpers of doUpdate). // template void updateValue (uInt row, const TableExprId& rowid, Bool isScalarCol, const TableExprNode& node, const Array& mask, Bool maskFirst, TableColumn& col, const Slicer* slicerPtr, ArrayColumn& maskCol); template void updateScalar (uInt row, const TableExprId& rowid, const TableExprNode& node, TableColumn& col); template void updateArray (uInt row, const TableExprId& rowid, const TableExprNode& node, const Array& res, ArrayColumn& col); template void updateSlice (uInt row, const TableExprId& rowid, const TableExprNode& node, const Array& res, const Slicer& slice, ArrayColumn& col); template void copyMaskedValue (uInt row, ArrayColumn& acol, const Slicer* slicerPtr, const TNODE* val, uInt incr, const Array& mask); Array makeMaskSlice (const Array& mask, Bool maskFirst, const IPosition& shapeCol, const Slicer* slicerPtr); void checkMaskColumn (Bool hasMask, const ArrayColumn& maskCol, const TableColumn& col); // // 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); // Get the order for this key. Use the default order_p if not // explicitly given with the key. Sort::Order getOrder (const TableParseSort& key) const; // 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; // Find a table for the given shorthand. // If no shorthand is given, the first table is returned (if there). // If not found, a null Table object is returned. Table findTable (const String& shorthand) const; // Handle the selection of a wildcarded column name. void handleWildColumn (Int stringType, const String& name); // 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 String& unitName); // Find the names of all stored columns in a table. Block getStoredColumns (const Table& tab) const; // Try to find the keyword representing a table in one of the tables // in any select block (from inner to outer). // If not found, an exception is thrown. static Table tableKey (const String& fullName, const String& shorthand, const String& columnName, const Vector& fieldNames, const vector& stack); // Try to find the keyword representing a table in the given table. // If the columnName is empty, the keyword is a table keyword. // If not found, a null Table object is returned. static Table findTableKey (const Table& table, const String& columnName, const Vector& keyNames); // Check if the tables used in selection columns have the same // size as the first table given in FROM. void checkTableProjSizes() 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 doGroupByAggrMultiple. template vector > doGroupByAggrSingleKey (const vector& aggrNodes) { // 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. 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 (uInt i=0; i::iterator iter = keyFuncMap.find (key); if (iter == keyFuncMap.end()) { groupnr = funcSets.size(); keyFuncMap[key] = groupnr; funcSets.push_back (new TableExprGroupFuncSet (aggrNodes)); } else { groupnr = iter->second; } } rowid.setRownr (rownrs_p[i]); funcSets[groupnr]->apply (rowid); } return funcSets; } // Create the set of aggregate functions and groupby keys in case // multiple keys are given. vector > doGroupByAggrMultipleKeys (const vector& aggrNodes); //# Command type. CommandType commandType_p; //# Table description for a series of column descriptions. TableDesc tableDesc_p; //# Vector of TableParse objects. //# This is needed for the functions above, otherwise they have no //# way to communicate. vector fromTables_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; //# Distinct values in output? Bool distinct_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; Record dminfo_p; //# Resulting set (from GIVING part). TableExprNodeSet* resultSet_p; //# The WHERE expression tree. TableExprNode node_p; //# The GROUPBY expressions. vector groupbyNodes_p; Bool groupbyRollup_p; //# use ROLLUP in GROUPBY? //# The HAVING expression. TableExprNode havingNode_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. TableParseSelect* 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. vector applySelNodes_p; //# The resulting table. Table table_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; //# The table resulting from a projection with expressions. Table projectExprTable_p; //# The projected columns used in the HAVING and ORDERBY clauses. Block projectExprSubset_p; Block projectExprSelColumn_p; //# The resulting row numbers. Vector rownrs_p; }; //# Implement the inline functions. inline Bool TableParse::test (const String& str) const { return (shorthand_p == str ? True : False); } inline const String& TableParse::shorthand() const { return shorthand_p; } inline const Table& TableParse::table() const { return table_p; } inline void TableParseUpdate::setColumnName (const String& name) { columnName_p = name; } inline void TableParseUpdate::setColumnNameMask (const String& name) { columnNameMask_p = name; } inline const String& TableParseUpdate::columnName() const { return columnName_p; } inline const String& TableParseUpdate::columnNameMask() const { return columnNameMask_p; } inline TableExprNodeIndex* TableParseUpdate::indexPtr() const { return indexPtr_p; } inline const TableExprNode& TableParseUpdate::indexNode() const { return indexNode_p; } inline const TableExprNode& TableParseUpdate::node() const { return node_p; } inline TableExprNode& TableParseUpdate::node() { return node_p; } inline void TableParseUpdate::adaptUnit (const Unit& columnUnit) { node_p.adaptUnit (columnUnit); } inline const TableExprNode& TableParseSort::node() const { return node_p; } inline Bool TableParseSort::orderGiven() const { return given_p; } inline Sort::Order TableParseSort::order() const { return order_p; } inline const Block& TableParseSelect::getColumnNames() const { return columnNames_p; } inline const Table& TableParseSelect::getTable() const { return table_p; } inline void TableParseSelect::addUpdate (TableParseUpdate* upd) { update_p.push_back (upd); } inline Sort::Order TableParseSelect::getOrder (const TableParseSort& key) const { return (key.orderGiven() ? key.order() : order_p); } } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/tables/TaQL/UDFBase.cc000066400000000000000000000201571321422335000170710ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: UDFBase.cc 21262 2012-09-07 12:38:36Z gervandiepen $ //# 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; Mutex UDFBase::theirMutex(Mutex::Recursive); UDFBase::UDFBase() : itsDataType (TableExprNodeRep::NTAny), itsNDim (-2), itsIsConstant (False), itsIsAggregate (False), itsApplySelection (True) {} UDFBase::~UDFBase() { for (uInt i=0; i& operands, const Table& table, const TaQLStyle& style) { // Link to the operands. itsOperands.resize (operands.size()); for (uInt i=0; ilink(); } setup (table, style); if (itsDataType == TableExprNodeRep::NTAny) { throw TableInvExpr ("UDFBase: data type not set by derived UDF class"); } if (itsNDim < -1) { throw TableInvExpr ("UDFBase: ndim not set by derived UDF class"); } } void UDFBase::getAggrNodes (vector& aggr) { for (uInt i=0; igetAggrNodes (aggr); } } void UDFBase::getColumnNodes (vector& cols) { for (uInt i=0; igetColumnNodes (cols); } } 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::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(); ScopedMutexLock 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"); } } } UDFBase* UDFBase::createUDF (const String& name, const TaQLStyle& style) { String fname(name); fname.downcase(); // Try to find the function. map::iterator 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); ScopedMutexLock 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. // Note that a libname is different from a function name because // it does not contain dots. theirRegistry[libname] = 0; } } // Try to find the function. iter = theirRegistry.find (fname); if (iter != theirRegistry.end()) { return iter->second (fname); } // Look up 'libname.*' to see if the UDF supports any function. iter = theirRegistry.find (libname + ".*"); if (iter != theirRegistry.end()) { return iter->second (fname); } } String unk; if (fname != sfname) { unk = " (=" + fname + ')'; } throw TableInvExpr ("TaQL function " + sfname + unk + " is unknown\n" " Check (DY)LD_LIBRARY_PATH matches the" " libraries used during the build of " + libname); } } // end namespace casacore-2.4.1/tables/TaQL/UDFBase.h000066400000000000000000000403421321422335000167310ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: UDFBase.h 21262 2012-09-07 12:38:36Z gervandiepen $ #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 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 nodes in the function operands representing an aggregate function. void getAggrNodes (vector& aggr); // Get the nodes in the function operands representing a table column. void getColumnNodes (vector& cols); private: // Set up the function object. virtual void setup (const Table& table, const TaQLStyle&) = 0; protected: // Get the operands. PtrBlock& 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); // 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 PtrBlock& arg, const Table& table, 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. PtrBlock itsOperands; TableExprNodeRep::NodeDataType itsDataType; Int itsNDim; IPosition itsShape; String itsUnit; 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 Mutex theirMutex; }; } // end namespace #endif casacore-2.4.1/tables/TaQL/test/000077500000000000000000000000001321422335000163235ustar00rootroot00000000000000casacore-2.4.1/tables/TaQL/test/CMakeLists.txt000066400000000000000000000025321321422335000210650ustar00rootroot00000000000000# 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 tExprUnitNode tExprNodeUDF tMArray tMArrayMath tMArrayUtil tRecordExpr tRecordGram tRecordGramTable tTableExprData tTableGram tTaQLNode ) # Only test scripts, no test programs. set (testscripts ttaql tTableGramCretab tTableGramAlttab tTableGramUpdate tTableGramMasked tTableGramNull tTableGramGroupAggr tTableGramGroupAggrAll tTableGramGroupAggrMaskAll ) # Some test sources include a test .h file. include_directories ( . ) foreach (test ${tests}) add_executable (${test} ${test}.cc) 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-2.4.1/tables/TaQL/test/tExprGroup.cc000066400000000000000000000445611321422335000207630ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: tExprNode.cc 21156 2011-12-12 07:57:36Z gervandiepen $ #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.getNodeRep())); CountedPtr 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.getNodeRep())); CountedPtr 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.getNodeRep())); CountedPtr 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.getNodeRep())); CountedPtr 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.getNodeRep())); 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::gvarianceFUNC, expr), recs, variance(vecd), "varianceInt"); check (TableExprNode::newFunctionNode(TableExprFuncNode::gstddevFUNC, 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::gvarianceFUNC, expr), recs, variance(vecd), "varianceDouble"); check (TableExprNode::newFunctionNode(TableExprFuncNode::gstddevFUNC, 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"); } 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-2.4.1/tables/TaQL/test/tExprGroupArray.cc000066400000000000000000000245751321422335000217650ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: tExprNode.cc 21156 2011-12-12 07:57:36Z gervandiepen $ #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 vector& recs, const Array& expVal, const String& str) { cout << "Test Bool " << str << endl; // Get the aggregation node. TableExprAggrNodeArray& aggr = const_cast (dynamic_cast(*expr.getNodeRep())); 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 vector& recs, const Array& expVal, const String& str) { cout << "Test Int " << str << endl; // Get the aggregation node. TableExprAggrNodeArray& aggr = const_cast (dynamic_cast(*expr.getNodeRep())); TableExprGroupExprId funcid(0); for (uInt i=0; i > funcSets; funcSets.push_back (new TableExprGroupFuncSet()); CountedPtr 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; } vector > > ids(1, funcid.getIds()); CountedPtr 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 vector& recs, const Array& expVal, const String& str) { cout << "Test Double " << str << endl; // Get the aggregation node. TableExprAggrNodeArray& aggr = const_cast (dynamic_cast(*expr.getNodeRep())); 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 vector& recs, const Array& expVal, const String& str) { cout << "Test DComplex " << str << endl; // Get the aggregation node. TableExprAggrNodeArray& aggr = const_cast (dynamic_cast(*expr.getNodeRep())); 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 vector& recs, const Array& expVal) { cout << "Test Double ghist " << endl; // Get the aggregation node. TableExprAggrNodeArray& aggr = const_cast (dynamic_cast(*expr.getNodeRep())); CountedPtr func = aggr.makeGroupAggrFunc(); for (uInt i=0; iapply (id); } func->finish(); MArray val = func->getArrayInt(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. 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. 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. 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, Table()), 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. 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-2.4.1/tables/TaQL/test/tExprNode.cc000066400000000000000000001536171321422335000205570ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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() == TpInt); 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() == TpInt); 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 sz", asin(esz1)); checkFailure ("tan sz", tan(esz1)); 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-2.4.1/tables/TaQL/test/tExprNodeSet.cc000066400000000000000000001033201321422335000212150ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include #include #include #include #include #include #include #include #include #include #include #include // // Test program for the ExprNodeSet selection classes. // void checkMatchBool (const TableExprNodeSetElem& tset, Bool val, Bool result) { cout << "checkMatchBool " << val << ' ' << result << endl; Bool res = False; tset.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.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.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.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.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.matchDate (&res, &val, 1, 0); AlwaysAssertExit (res == result); } 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 (tset.dataType() == TableExprNodeRep::NTBool); AlwaysAssertExit (tset.isDiscrete()); AlwaysAssertExit (tset.isSingle()); AlwaysAssertExit (tset.start() != 0); AlwaysAssertExit (tset.end() == 0); AlwaysAssertExit (tset.increment() == 0); Vector vec; Int64 cnt=0; tset.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 (tset.dataType() == TableExprNodeRep::NTInt); AlwaysAssertExit (tset.isDiscrete()); AlwaysAssertExit (!tset.isSingle()); AlwaysAssertExit (tset.start() != 0); AlwaysAssertExit (tset.end() != 0); AlwaysAssertExit (tset.increment() != 0); Vector vec; Int64 cnt=0; tset.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 (tset.dataType() == TableExprNodeRep::NTInt); AlwaysAssertExit (tset.isDiscrete()); AlwaysAssertExit (!tset.isSingle()); AlwaysAssertExit (tset.start() != 0); AlwaysAssertExit (tset.end() != 0); AlwaysAssertExit (tset.increment() != 0); Vector vec; Int64 cnt=0; tset.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 (tset.dataType() == TableExprNodeRep::NTInt); AlwaysAssertExit (tset.isDiscrete()); AlwaysAssertExit (!tset.isSingle()); AlwaysAssertExit (tset.start() != 0); AlwaysAssertExit (tset.end() == 0); AlwaysAssertExit (tset.increment() != 0); Vector vec; Int64 cnt=0; tset.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 (tset.isDiscrete()); AlwaysAssertExit (!tset.isSingle()); AlwaysAssertExit (tset.start() != 0); AlwaysAssertExit (tset.end() != 0); AlwaysAssertExit (tset.increment() != 0); Vector vec(51); vec[0] = -3; vec[1] = -1; Int64 cnt=2; tset.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 (tset.isDiscrete()); AlwaysAssertExit (!tset.isSingle()); AlwaysAssertExit (tset.start() != 0); AlwaysAssertExit (tset.end() != 0); AlwaysAssertExit (tset.increment() == 0); Vector vec(98); Int64 cnt=0; tset.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 (tset.isDiscrete()); AlwaysAssertExit (!tset.isSingle()); AlwaysAssertExit (tset.start() != 0); AlwaysAssertExit (tset.end() == 0); AlwaysAssertExit (tset.increment() == 0); Vector vec; Int64 cnt=0; tset.fillVector (vec, cnt, 0); vec.resize (cnt, True); AlwaysAssertExit (allEQ(Vector(), vec)); } { TableExprNodeSetElem tset(0, &end, 0, False); AlwaysAssertExit (tset.isDiscrete()); AlwaysAssertExit (!tset.isSingle()); AlwaysAssertExit (tset.start() == 0); AlwaysAssertExit (tset.end() != 0); AlwaysAssertExit (tset.increment() == 0); Vector vec; Int64 cnt=0; tset.fillVector (vec, cnt, 0); vec.resize (cnt, True); Vector exp(100); indgen (exp); AlwaysAssertExit (allEQ(exp, vec)); } { TableExprNodeSetElem tset(0, 0, 0, True); AlwaysAssertExit (tset.isDiscrete()); AlwaysAssertExit (!tset.isSingle()); AlwaysAssertExit (tset.start() == 0); AlwaysAssertExit (tset.end() == 0); AlwaysAssertExit (tset.increment() == 0); Vector vec; Int64 cnt=0; tset.fillVector (vec, cnt, 0); vec.resize (cnt, True); AlwaysAssertExit (allEQ(Vector(), vec)); } { TableExprNodeSetElem tset(st); AlwaysAssertExit (tset.isDiscrete()); AlwaysAssertExit (tset.isSingle()); AlwaysAssertExit (tset.start() != 0); AlwaysAssertExit (tset.end() == 0); AlwaysAssertExit (tset.increment() == 0); Vector vec; Int64 cnt=0; tset.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 (tset.dataType() == TableExprNodeRep::NTDouble); AlwaysAssertExit (tset.unit() == "dm"); AlwaysAssertExit (tset.isDiscrete()); AlwaysAssertExit (!tset.isSingle()); AlwaysAssertExit (tset.start() != 0); AlwaysAssertExit (tset.end() != 0); AlwaysAssertExit (tset.increment() != 0); Vector vec; Int64 cnt=0; tset.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 (tset.dataType() == TableExprNodeRep::NTDouble); AlwaysAssertExit (tset.isDiscrete()); AlwaysAssertExit (!tset.isSingle()); AlwaysAssertExit (tset.start() != 0); AlwaysAssertExit (tset.end() != 0); AlwaysAssertExit (tset.increment() != 0); Vector vec; Int64 cnt=0; tset.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 (tset.dataType() == TableExprNodeRep::NTDouble); AlwaysAssertExit (tset.isDiscrete()); AlwaysAssertExit (!tset.isSingle()); AlwaysAssertExit (tset.start() != 0); AlwaysAssertExit (tset.end() == 0); AlwaysAssertExit (tset.increment() != 0); Vector vec; Int64 cnt=0; tset.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 (tset.isDiscrete()); AlwaysAssertExit (!tset.isSingle()); AlwaysAssertExit (tset.start() != 0); AlwaysAssertExit (tset.end() != 0); AlwaysAssertExit (tset.increment() != 0); Vector vec(25); vec[0] = 0.6; vec[1] = 0.8; Int64 cnt=2; tset.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 (tset.isDiscrete()); AlwaysAssertExit (!tset.isSingle()); AlwaysAssertExit (tset.start() != 0); AlwaysAssertExit (tset.end() != 0); AlwaysAssertExit (tset.increment() == 0); Vector vec; Int64 cnt=0; tset.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 (tset.isDiscrete()); AlwaysAssertExit (!tset.isSingle()); AlwaysAssertExit (tset.start() != 0); AlwaysAssertExit (tset.end() == 0); AlwaysAssertExit (tset.increment() == 0); Vector vec; Int64 cnt=0; tset.fillVector (vec, cnt, 0); vec.resize (cnt, True); AlwaysAssertExit (allNear(Vector(), vec, 1e-13)); } { TableExprNodeSetElem tset(st); AlwaysAssertExit (tset.isDiscrete()); AlwaysAssertExit (tset.isSingle()); AlwaysAssertExit (tset.start() != 0); AlwaysAssertExit (tset.end() == 0); AlwaysAssertExit (tset.increment() == 0); Vector vec; Int64 cnt=0; tset.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 (tset.dataType() == TableExprNodeRep::NTComplex); AlwaysAssertExit (tset.isDiscrete()); AlwaysAssertExit (tset.isSingle()); AlwaysAssertExit (tset.start() != 0); AlwaysAssertExit (tset.end() == 0); AlwaysAssertExit (tset.increment() == 0); Vector vec; Int64 cnt=0; tset.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 (tset.dataType() == TableExprNodeRep::NTString); AlwaysAssertExit (tset.isDiscrete()); AlwaysAssertExit (tset.isSingle()); AlwaysAssertExit (tset.start() != 0); AlwaysAssertExit (tset.end() == 0); AlwaysAssertExit (tset.increment() == 0); Vector vec; Int64 cnt=0; tset.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 (tset.dataType() == TableExprNodeRep::NTDate); AlwaysAssertExit (tset.isDiscrete()); AlwaysAssertExit (!tset.isSingle()); AlwaysAssertExit (tset.start() != 0); AlwaysAssertExit (tset.end() != 0); AlwaysAssertExit (tset.increment() != 0); Vector vect; Int64 cnt=0; tset.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 (tset.dataType() == TableExprNodeRep::NTDate); AlwaysAssertExit (tset.isDiscrete()); AlwaysAssertExit (!tset.isSingle()); AlwaysAssertExit (tset.start() != 0); AlwaysAssertExit (tset.end() != 0); AlwaysAssertExit (tset.increment() != 0); Vector vect; Int64 cnt=0; tset.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 (tset.isDiscrete()); AlwaysAssertExit (!tset.isSingle()); AlwaysAssertExit (tset.start() != 0); AlwaysAssertExit (tset.end() != 0); AlwaysAssertExit (tset.increment() == 0); Vector vect; Int64 cnt=0; tset.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 (tset.isDiscrete()); AlwaysAssertExit (!tset.isSingle()); AlwaysAssertExit (tset.start() != 0); AlwaysAssertExit (tset.end() == 0); AlwaysAssertExit (tset.increment() == 0); Vector vect; Int64 cnt=0; tset.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 (tset.isDiscrete()); AlwaysAssertExit (tset.isSingle()); AlwaysAssertExit (tset.start() != 0); AlwaysAssertExit (tset.end() == 0); AlwaysAssertExit (tset.increment() == 0); Vector vect; Int64 cnt=0; tset.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 (tset.dataType() == TableExprNodeRep::NTDouble); AlwaysAssertExit (!tset.isDiscrete()); AlwaysAssertExit (!tset.isSingle()); } { TableExprNode st(1); TableExprNode end(99.); TableExprNodeSetElem tset(False, st, end, False); AlwaysAssertExit (tset.dataType() == TableExprNodeRep::NTDouble); } } void doIntervalDouble() { TableExprNode st(1); st.useUnit ("m"); TableExprNode end(989.5); end.useUnit ("cm"); { TableExprNodeSetElem tset(False, st, end, False); AlwaysAssertExit (tset.dataType() == TableExprNodeRep::NTDouble); AlwaysAssertExit (!tset.isDiscrete()); AlwaysAssertExit (!tset.isSingle()); AlwaysAssertExit (!tset.isLeftClosed()); AlwaysAssertExit (!tset.isRightClosed()); 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 (tset.dataType() == TableExprNodeRep::NTDouble); AlwaysAssertExit (!tset.isDiscrete()); AlwaysAssertExit (!tset.isSingle()); AlwaysAssertExit (tset.isLeftClosed()); AlwaysAssertExit (tset.isRightClosed()); 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 (tset.dataType() == TableExprNodeRep::NTDouble); AlwaysAssertExit (!tset.isDiscrete()); AlwaysAssertExit (!tset.isSingle()); AlwaysAssertExit (tset.isLeftClosed()); AlwaysAssertExit (!tset.isRightClosed()); 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 (tset.dataType() == TableExprNodeRep::NTDouble); AlwaysAssertExit (!tset.isDiscrete()); AlwaysAssertExit (!tset.isSingle()); AlwaysAssertExit (!tset.isLeftClosed()); AlwaysAssertExit (tset.isRightClosed()); 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 (tset.dataType() == TableExprNodeRep::NTString); AlwaysAssertExit (!tset.isDiscrete()); AlwaysAssertExit (!tset.isSingle()); 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 (tset.dataType() == TableExprNodeRep::NTString); AlwaysAssertExit (!tset.isDiscrete()); AlwaysAssertExit (!tset.isSingle()); 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 (tset.dataType() == TableExprNodeRep::NTString); AlwaysAssertExit (!tset.isDiscrete()); AlwaysAssertExit (!tset.isSingle()); 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 (tset.dataType() == TableExprNodeRep::NTString); AlwaysAssertExit (!tset.isDiscrete()); AlwaysAssertExit (!tset.isSingle()); 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 (tset.dataType() == TableExprNodeRep::NTDate); AlwaysAssertExit (!tset.isDiscrete()); AlwaysAssertExit (!tset.isSingle()); 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 (tset.dataType() == TableExprNodeRep::NTDate); AlwaysAssertExit (!tset.isDiscrete()); AlwaysAssertExit (!tset.isSingle()); 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 (tset.dataType() == TableExprNodeRep::NTDate); AlwaysAssertExit (!tset.isDiscrete()); AlwaysAssertExit (!tset.isSingle()); 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 (tset.dataType() == TableExprNodeRep::NTDate); AlwaysAssertExit (!tset.isDiscrete()); AlwaysAssertExit (!tset.isSingle()); 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 doSetBool() { TableExprNodeSetElem elem((TableExprNode(True))); TableExprNodeSet set; set.add (elem); set.add (elem); AlwaysAssertExit (set.isSingle()); AlwaysAssertExit (set.isDiscrete()); AlwaysAssertExit (set.isBounded()); AlwaysAssertExit (set.nelements() == 2); AlwaysAssertExit (!set.hasArrays()); AlwaysAssertExit (set.hasBool (0, True)); AlwaysAssertExit (!set.hasBool (0, False)); } void doSetInt() { TableExprNodeSetElem elem((TableExprNode(1))); TableExprNodeSet set; set.add (elem); AlwaysAssertExit (set.isSingle()); AlwaysAssertExit (set.isDiscrete()); AlwaysAssertExit (set.isBounded()); AlwaysAssertExit (set.nelements() == 1); AlwaysAssertExit (!set.hasArrays()); AlwaysAssertExit (set.hasInt (0, 1)); AlwaysAssertExit (!set.hasInt (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.nelements() == 2); AlwaysAssertExit (!set.hasArrays()); AlwaysAssertExit (set.hasInt (0, 1)); AlwaysAssertExit (!set.hasInt (0, 2)); AlwaysAssertExit (set.hasInt (0, 3)); AlwaysAssertExit (!set.hasInt (0, 4)); set.add (TableExprNodeSetElem(&st, 0, 0, True)); AlwaysAssertExit (!set.isSingle()); AlwaysAssertExit (set.isDiscrete()); AlwaysAssertExit (!set.isBounded()); AlwaysAssertExit (set.nelements() == 3); AlwaysAssertExit (!set.hasArrays()); AlwaysAssertExit (set.hasInt (0, 1)); AlwaysAssertExit (!set.hasInt (0, 2)); AlwaysAssertExit (set.hasInt (0, 3)); AlwaysAssertExit (set.hasInt (0, 4)); } void doSetDouble() { TableExprNodeSetElem elem((TableExprNode(1.))); TableExprNodeSet set; set.add (elem); AlwaysAssertExit (set.isSingle()); AlwaysAssertExit (set.isDiscrete()); AlwaysAssertExit (set.isBounded()); AlwaysAssertExit (set.nelements() == 1); AlwaysAssertExit (!set.hasArrays()); AlwaysAssertExit (set.hasDouble (0, 1)); AlwaysAssertExit (!set.hasDouble (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.nelements() == 2); AlwaysAssertExit (!set.hasArrays()); AlwaysAssertExit (set.hasDouble (0, 1)); AlwaysAssertExit (!set.hasDouble (0, 2)); AlwaysAssertExit (set.hasDouble (0, 3)); AlwaysAssertExit (!set.hasDouble (0, 4)); set.add (TableExprNodeSetElem(&st, 0, 0, True)); AlwaysAssertExit (!set.isSingle()); AlwaysAssertExit (set.isDiscrete()); AlwaysAssertExit (!set.isBounded()); AlwaysAssertExit (set.nelements() == 3); AlwaysAssertExit (!set.hasArrays()); AlwaysAssertExit (set.hasDouble (0, 1)); AlwaysAssertExit (!set.hasDouble (0, 2)); AlwaysAssertExit (set.hasDouble (0, 3)); AlwaysAssertExit (set.hasDouble (0, 4)); } void doSetDComplex() { 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.nelements() == 2); AlwaysAssertExit (!set.hasArrays()); AlwaysAssertExit (set.hasDComplex (0, DComplex(1,2))); AlwaysAssertExit (!set.hasDComplex (0, DComplex(1,3))); } void doSetString() { TableExprNodeSetElem elem((TableExprNode("ger"))); TableExprNodeSet set; set.add (elem); AlwaysAssertExit (set.isSingle()); AlwaysAssertExit (set.isDiscrete()); AlwaysAssertExit (set.isBounded()); AlwaysAssertExit (set.nelements() == 1); AlwaysAssertExit (!set.hasArrays()); AlwaysAssertExit (set.hasString (0, "ger")); AlwaysAssertExit (!set.hasString (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.nelements() == 2); AlwaysAssertExit (!set.hasArrays()); AlwaysAssertExit (set.hasString (0, "ger")); AlwaysAssertExit (!set.hasString (0, "Ger")); AlwaysAssertExit (set.hasString (0, "ger1")); AlwaysAssertExit (!set.hasString (0, "ger99")); set.add (TableExprNodeSetElem(True, st)); AlwaysAssertExit (!set.isSingle()); AlwaysAssertExit (!set.isDiscrete()); AlwaysAssertExit (!set.isBounded()); AlwaysAssertExit (set.nelements() == 3); AlwaysAssertExit (!set.hasArrays()); AlwaysAssertExit (set.hasString (0, "ger")); AlwaysAssertExit (!set.hasString (0, "Ger")); AlwaysAssertExit (set.hasString (0, "ger1")); AlwaysAssertExit (set.hasString (0, "ger99")); } void doSetDate() { TableExprNodeSetElem elem((TableExprNode(1.))); TableExprNodeSet set; set.add (elem); AlwaysAssertExit (set.isSingle()); AlwaysAssertExit (set.isDiscrete()); AlwaysAssertExit (set.isBounded()); AlwaysAssertExit (set.nelements() == 1); AlwaysAssertExit (!set.hasArrays()); AlwaysAssertExit (set.hasDouble (0, 1)); AlwaysAssertExit (!set.hasDouble (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.nelements() == 2); AlwaysAssertExit (!set.hasArrays()); AlwaysAssertExit (set.hasDouble (0, 1)); AlwaysAssertExit (!set.hasDouble (0, 2)); AlwaysAssertExit (set.hasDouble (0, 3)); AlwaysAssertExit (!set.hasDouble (0, 4)); set.add (TableExprNodeSetElem(&st, 0, 0, True)); AlwaysAssertExit (!set.isSingle()); AlwaysAssertExit (set.isDiscrete()); AlwaysAssertExit (!set.isBounded()); AlwaysAssertExit (set.nelements() == 3); AlwaysAssertExit (!set.hasArrays()); AlwaysAssertExit (set.hasDouble (0, 1)); AlwaysAssertExit (!set.hasDouble (0, 2)); AlwaysAssertExit (set.hasDouble (0, 3)); AlwaysAssertExit (set.hasDouble (0, 4)); } 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.nelements() == 3); AlwaysAssertExit (!set.hasArrays()); AlwaysAssertExit (set.hasInt (0, 4)); AlwaysAssertExit (set.hasInt (0, 5)); AlwaysAssertExit (set.hasInt (0, 6)); AlwaysAssertExit (!set.hasInt (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.nelements() == 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.nelements() == 2); AlwaysAssertExit (!set.hasArrays()); AlwaysAssertExit (set[0].start() == 0); cout << set[0].end()->getInt(0) << endl; 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() == 0); 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.nelements() == 0); AlwaysAssertExit (!set.hasArrays()); } int main() { try { doDiscreteBool(); doDiscreteInt(); doDiscreteDouble(); doDiscreteDComplex(); doDiscreteString(); doDiscreteDate(); doIntervalBool(); doIntervalInt(); doIntervalDouble(); doIntervalDComplex(); doIntervalString(); doIntervalDate(); doSetBool(); doSetInt(); doSetDouble(); doSetDComplex(); doSetString(); doSetDate(); doIPosition(); doSlicer(); doEmpty(); } 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-2.4.1/tables/TaQL/test/tExprNodeUDF.cc000066400000000000000000000131621321422335000211040ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# Includes #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 vector& ids = aid.result().ids(id.rownr()); 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; } }; 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.getNodeRep()); vector aggrNodes; rep->getAggrNodes (aggrNodes); AlwaysAssertExit (aggrNodes.size() == 1); AlwaysAssertExit (aggrNodes[0]->isLazyAggregate()); CountedPtr > ids(new vector()); for (uInt i=0; ipush_back (TableExprId(i)); } vector > > idVec(1, ids); vector > funcVec(1); CountedPtr 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-2.4.1/tables/TaQL/test/tExprUnitNode.cc000066400000000000000000000206261321422335000214100ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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() == TpInt); 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.02, "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 AipsError& x) { err = True; cout << x.what() << endl; } AlwaysAssertExit (err); err = False; try { MArray a1(arr, Array(IPosition(1,1)), True); } catch (const AipsError& x) { err = True; cout << x.what() << endl; } AlwaysAssertExit (err); err = False; try { MArray a1(arr); a1.setMask (Array(IPosition(1,2))); } catch (const AipsError& 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 (AipsError& x) { cout << "Unexpected exception: " << x.what() << endl; return 1; } cout << "OK" << endl; return 0; } casacore-2.4.1/tables/TaQL/test/tMArrayMath.cc000066400000000000000000000771421321422335000210360ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: tMArrayMath.cc 21262 2012-09-07 12:38:36Z gervandiepen $ #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)); } } 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), variance(v))); AlwaysAssertExit (near(variance(ma, mn), variance(v, mn))); AlwaysAssertExit (near(stddev(ma), stddev(v))); AlwaysAssertExit (near(stddev(ma, mn), 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), variance(v1))); AlwaysAssertExit (near(variance(ma, mn), variance(v1, mn))); AlwaysAssertExit (near(stddev(ma), stddev(v1))); AlwaysAssertExit (near(stddev(ma, mn), 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)).array(), partialVariances (arr, IPosition(1,1)))); AlwaysAssertExit (allEQ(partialStddevs(MArray(arr), IPosition(1,1)).array(), partialStddevs (arr, IPosition(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)); AlwaysAssertExit (allNear(mad.array(), aresd, 1e-5) && allEQ(mad.mask(), mres)); aresd(1,1) = 70.7107; mad = partialStddevs(MArray(arrd,mask), IPosition(1,0)); 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)).array(), boxedArrayMath (arr, IPosition(2,2,2,2), VarianceFunc()))); AlwaysAssertExit (allEQ(boxedStddevs(MArray(arr), IPosition(2,2,2,2)).array(), boxedArrayMath (arr, IPosition(2,2,2,2), StddevFunc()))); 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)); 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)); 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(), False); AlwaysAssertExit (allNear(slidingVariances(MArray(arrd), IPosition(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), False); AlwaysAssertExit (allNear(mad.array(), aresd, 1e-5) && allEQ(mad.mask(), mres)); aresd = slidingArrayMath(arrd, IPosition(1,1), StddevFunc(), False); AlwaysAssertExit (allNear(slidingStddevs(MArray(arrd), IPosition(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), 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 << "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 (AipsError& x) { cout << "Unexpected exception: " << x.what() << endl; return 1; } return 0; } casacore-2.4.1/tables/TaQL/test/tMArrayUtil.cc000066400000000000000000000067021321422335000210540ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: tMArrayUtil.cc 21273 2012-10-31 15:25:10Z gervandiepen $ #include #include #include #include #include using namespace casacore; using namespace std; int main() { // 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()); // Check if a reordered null array is null. AlwaysAssertExit (reorderArray (MArray(), IPosition()).isNull()); return 0; } casacore-2.4.1/tables/TaQL/test/tRecordExpr.cc000066400000000000000000000125151321422335000210770ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { cout << "Unexpected exception: " << x.getMesg() << endl; return 1; } catch (...) { cout << "Unexpected unknown exception" << endl; return 1; } return 0; } casacore-2.4.1/tables/TaQL/test/tRecordGram.cc000066400000000000000000000156721321422335000210560ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { cout << "Expected exception:\n" << x.getMesg() << endl; err = True; } AlwaysAssertExit (err); } } int main() { try { doIt(); } catch (AipsError x) { cout << "Unexpected exception: " << x.getMesg() << endl; return 1; } catch (...) { cout << "Unexpected unknown exception" << endl; return 1; } cout << "OK" << endl; return 0; } casacore-2.4.1/tables/TaQL/test/tRecordGramTable.cc000066400000000000000000000104021321422335000220100ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 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 (uInt 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 (AipsError& x) { cout << x.getMesg() << endl; } } } int main (int argc, const char* argv[]) { if (argc < 2) { docomm(); return 0; } try { doIt(argv[1]); } catch (AipsError x) { cout << "Unexpected exception: " << x.getMesg() << endl; return 1; } catch (...) { cout << "Unexpected unknown exception" << endl; return 1; } return 0; } casacore-2.4.1/tables/TaQL/test/tRecordGramTable.out000066400000000000000000000061441321422335000222420ustar00rootroot00000000000000ab: [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-2.4.1/tables/TaQL/test/tRecordGramTable.run000077500000000000000000000015211321422335000222340ustar00rootroot00000000000000#!/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-2.4.1/tables/TaQL/test/tTaQLNode.cc000066400000000000000000000071251321422335000204320ustar00rootroot00000000000000//# tTaQLNode.cc: This program tests 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 seltab (const String&); void docomm (); int main (int argc, const char* argv[]) { try { if (argc > 1) { seltab(argv[1]); }else{ docomm(); } } catch (AipsError x) { cout << "\nCaught an exception: " << x.getMesg() << 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 (AipsError x) { cout << x.getMesg() << endl; } } } // Sort and select data. void seltab (const String& str) { cout << str << endl; TaQLNode node; try { // Parse and execute the command. node = TaQLNode::parse (str); } catch (AipsError& x) { cout << x.getMesg() << 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 and execute the command. node1 = TaQLNode::parse (oss.str()); } catch (AipsError& x) { cout << x.getMesg() << 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. MemoryIO mio; AipsIO aio(&mio); 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-2.4.1/tables/TaQL/test/tTaQLNode.out000066400000000000000000000425141321422335000206550ustar00rootroot00000000000000select 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 select from a.tab #comment 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 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 [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 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 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]]) calc from $1 calc sum([select ab from $1]) 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 update tTaQLNode_tmp.tab set ab=sum(arr1)+ac*2, arr1=arr1+2 where ac>3 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 delete from tTaQLNode_tmp.tab orderby desc ab limit 1 offset 2 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 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 [(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'] count ab,ac+1 from tTaQLnode_tmp.tab 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] create table tTaQLNode_tmp.tab limit 10*10 CREATE TABLE tTaQLNode_tmp.tab LIMIT (10)*(10) 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 rename column old to new 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=[=]] casacore-2.4.1/tables/TaQL/test/tTaQLNode.run000077500000000000000000000301351321422335000206510ustar00rootroot00000000000000#!/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 '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 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 [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 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 '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 '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 '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 '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 '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 'create table tTaQLNode_tmp.tab limit 10*10' $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 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=[=]]' casacore-2.4.1/tables/TaQL/test/tTableExprData.cc000066400000000000000000000117511321422335000215030ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { cout << "Unexpected exception: " << x.getMesg() << endl; return 1; } catch (...) { cout << "Unexpected unknown exception" << endl; return 1; } cout << "OK" << endl; return 0; } casacore-2.4.1/tables/TaQL/test/tTableGram.cc000066400000000000000000000271711321422335000206640ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { cout << "\nCaught an exception: " << x.getMesg() << 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 (AipsError x) { cout << x.getMesg() << 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()); uInt i; for (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 (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 (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. 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 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 (uInt 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(); } 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=="select" || s=="update" || s=="insert" || s=="calc" || s=="delete" || s=="count" || s=="create" || s=="createtable" || s=="alter" || s=="altertable" || s=="using" || s=="usingstyle" || s=="time"); } } String strc(str); if (addCalc) { strc = "CALC " + str; } cout << strc << endl; // Parse and execute the command. TaQLResult result; Table* tabp = 0; uInt i; 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 (i=0; i 0) { showtab (*tabp, vecstr); } } else { showExpr (result.node()); } delete tabp; } casacore-2.4.1/tables/TaQL/test/tTableGram.out000066400000000000000000000716061321422335000211100ustar00rootroot00000000000000testing 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 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 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 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 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 [4] using style glish calc [3:6][3] has been executed [5] using style base0 calc [3:6][3] has been executed [6] using style base1 calc [3:6][3] has been executed [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 casacore-2.4.1/tables/TaQL/test/tTableGram.run000066400000000000000000000466711321422335000211110ustar00rootroot00000000000000#!/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 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 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]))' $casa_checktool ./tTableGram 'select ab from $1;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 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' casacore-2.4.1/tables/TaQL/test/tTableGramAlttab.out000066400000000000000000000213551321422335000222340ustar00rootroot00000000000000 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 ActualCacheSize=1 PERSCACHESIZE=1 col1 Int shape=[2,3] unit=[m] StandardStMan file=table.f1 name=StandardStMan bucketsize=128 ActualCacheSize=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 ActualCacheSize=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 ActualCacheSize=1 PERSCACHESIZE=1 col1 Int shape=[2,3] unit=[m] StandardStMan file=table.f1 name=StandardStMan bucketsize=128 ActualCacheSize=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 ActualCacheSize=1 PERSCACHESIZE=1 col1 Int shape=[2,3] unit=[m] StandardStMan file=table.f1 name=StandardStMan bucketsize=128 ActualCacheSize=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 ActualCacheSize=1 PERSCACHESIZE=1 col1 Int shape=[2,3] unit=[m] StandardStMan file=table.f1 name=StandardStMan bucketsize=128 ActualCacheSize=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 ActualCacheSize=1 PERSCACHESIZE=1 col1 Int shape=[2,3] unit=[m] StandardStMan file=table.f1 name=StandardStMan bucketsize=128 ActualCacheSize=2 PERSCACHESIZE=2 IndexLength=118 col2 Bool scalar StandardStMan file=table.f2 name=SSM bucketsize=260 ActualCacheSize=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 ActualCacheSize=1 PERSCACHESIZE=1 col1 Int shape=[2,3] unit=[m] StandardStMan file=table.f1 name=StandardStMan bucketsize=128 ActualCacheSize=2 PERSCACHESIZE=2 IndexLength=118 col2 Bool scalar StandardStMan file=table.f2 name=SSM bucketsize=260 ActualCacheSize=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 ActualCacheSize=1 PERSCACHESIZE=1 col1 Int shape=[2,3] unit=[m] StandardStMan file=table.f2 name=SSM bucketsize=260 ActualCacheSize=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 casacore-2.4.1/tables/TaQL/test/tTableGramAlttab.run000066400000000000000000000077711321422335000222370ustar00rootroot00000000000000#!/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. 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/' 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/' 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/' 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/' 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/' 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/' $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/' $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' # Remove the symlink rm -f tTableGramAlttab casacore-2.4.1/tables/TaQL/test/tTableGramCretab.out000066400000000000000000000102201321422335000222120ustar00rootroot00000000000000 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 ActualCacheSize=1 PERSCACHESIZE=1 col1 Int shape=[2,3] unit=[m] StandardStMan file=table.f1 name=StandardStMan bucketsize=128 ActualCacheSize=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] has been executed cretab result of 0 rows 2 selected columns: col1 col2 Structure of table tTableGramCretab_tmp.tab2 ------------------ 0 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 ActualCacheSize=1 PERSCACHESIZE=1 col1 Int shape=[2,3] unit=[m] StandardStMan file=table.f1 name=StandardStMan bucketsize=128 ActualCacheSize=2 PERSCACHESIZE=2 IndexLength=118 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 ActualCacheSize=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 ActualCacheSize=1 PERSCACHESIZE=1 col1x Int shape=[3,4] col2y Bool scalar casacore-2.4.1/tables/TaQL/test/tTableGramCretab.run000066400000000000000000000053321321422335000222170ustar00rootroot00000000000000#!/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/' 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]' ../../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/' # Remove the symlink rm -f tTableGramCretab casacore-2.4.1/tables/TaQL/test/tTableGramEpoch.out000066400000000000000000000112101321422335000220500ustar00rootroot00000000000000select 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-2.4.1/tables/TaQL/test/tTableGramEpoch.run000066400000000000000000000043021321422335000220510ustar00rootroot00000000000000#!/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-2.4.1/tables/TaQL/test/tTableGramGroupAggr.out000066400000000000000000000145361321422335000227250ustar00rootroot00000000000000testing groupby/aggregate ... select gcount(*) from tTableGramGroupAggr_tmp.tab has been executed select result of 1 rows 1 selected columns: Col_1 10 select gsum(ab) from tTableGramGroupAggr_tmp.tab has been executed select result of 1 rows 1 selected columns: Col_1 45 select gaggr(ab) from tTableGramGroupAggr_tmp.tab has been executed select result of 1 rows 1 selected columns: Col_1 shape=[10] select ab, gfirst(ab), glast(ab), gsum(ab), gaggr(ab) from tTableGramGroupAggr_tmp.tab has been executed select result of 1 rows 5 selected columns: ab Col_2 Col_3 Col_4 Col_5 9 0 9 45 shape=[10] select gcount() from tTableGramGroupAggr_tmp.tab groupby ab,ac,ad,ae,af has been executed select result of 10 rows 1 selected columns: Col_1 1 1 1 1 1 1 1 1 1 1 select ab from tTableGramGroupAggr_tmp.tab groupby ab has been executed select result of 10 rows 1 selected columns: ab 0 1 2 3 4 5 6 7 8 9 select gsum(ab) from tTableGramGroupAggr_tmp.tab groupby ab has been executed select result of 10 rows 1 selected columns: Col_1 0 1 2 3 4 5 6 7 8 9 select gaggr(ab) from tTableGramGroupAggr_tmp.tab groupby ab has been executed select result of 10 rows 1 selected columns: Col_1 shape=[1] shape=[1] shape=[1] shape=[1] shape=[1] shape=[1] shape=[1] shape=[1] shape=[1] shape=[1] select ab, gfirst(ab), glast(ab), gsum(ab), gaggr(ab) from tTableGramGroupAggr_tmp.tab where ab%2 = 0 groupby ab//4 has been executed select result of 3 rows 5 selected columns: ab Col_2 Col_3 Col_4 Col_5 2 0 2 2 shape=[2] 6 4 6 10 shape=[2] 8 8 8 8 shape=[1] select ab from tTableGramGroupAggr_tmp.tab where ab>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-2.4.1/tables/TaQL/test/tTableGramGroupAggr.run000066400000000000000000000143411321422335000227140ustar00rootroot00000000000000#!/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-2.4.1/tables/TaQL/test/tTableGramGroupAggrAll.out000066400000000000000000000536561321422335000233640ustar00rootroot00000000000000*** 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 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 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 Caught an exception: Error in TaQL command: 'select variance(gaggr(ag)) as A, gvariance(ag) as B from tTableGramGroupAggrAll_tmp.tab having A!~=B' Error in select expression: Erroneous use of function variance - invalid operand data type; function argument is not real select stddev(gaggr(ag)) as A, gstddev(ag) as B from tTableGramGroupAggrAll_tmp.tab having A!~=B Caught an exception: Error in TaQL command: 'select stddev(gaggr(ag)) as A, gstddev(ag) as B from tTableGramGroupAggrAll_tmp.tab having A!~=B' Error in select expression: Erroneous use of function stddev - invalid operand data type; function argument is not real 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 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 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 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 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 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 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,0))) as A, gmin(complex(arr1,0)) as B from tTableGramGroupAggrAll_tmp.tab having A!~=B Caught an exception: Error in TaQL command: 'select min(gaggr(complex(arr1,0))) as A, gmin(complex(arr1,0)) 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,0))) as A, gmax(complex(arr1,0)) as B from tTableGramGroupAggrAll_tmp.tab having A!~=B Caught an exception: Error in TaQL command: 'select max(gaggr(complex(arr1,0))) as A, gmax(complex(arr1,0)) 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,0))) as A, gsum(complex(arr1,0)) 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,0))) as A, gproduct(complex(arr1,0)) 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,0))) as A, gsumsqr(complex(arr1,0)) 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,0))) as A, gmean(complex(arr1,0)) 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,0))) as A, gvariance(complex(arr1,0)) as B from tTableGramGroupAggrAll_tmp.tab having A!~=B Caught an exception: Error in TaQL command: 'select variance(gaggr(complex(arr1,0))) as A, gvariance(complex(arr1,0)) as B from tTableGramGroupAggrAll_tmp.tab having A!~=B' Error in select expression: Erroneous use of function variance - invalid operand data type; function argument is not real select stddev(gaggr(complex(arr1,0))) as A, gstddev(complex(arr1,0)) as B from tTableGramGroupAggrAll_tmp.tab having A!~=B Caught an exception: Error in TaQL command: 'select stddev(gaggr(complex(arr1,0))) as A, gstddev(complex(arr1,0)) as B from tTableGramGroupAggrAll_tmp.tab having A!~=B' Error in select expression: Erroneous use of function stddev - invalid operand data type; function argument is not real select rms(gaggr(complex(arr1,0))) as A, grms(complex(arr1,0)) as B from tTableGramGroupAggrAll_tmp.tab having A!~=B Caught an exception: Error in TaQL command: 'select rms(gaggr(complex(arr1,0))) as A, grms(complex(arr1,0)) 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,0)), 0.4) as A, gfractile(complex(arr1,0),0.4) as B from tTableGramGroupAggrAll_tmp.tab having A!~=B Caught an exception: Error in TaQL command: 'select fractile(gaggr(complex(arr1,0)), 0.4) as A, gfractile(complex(arr1,0),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 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 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 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 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,0)),0) as A, gmins(complex(arr1,0)) 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,0)),0) as A, gmins(complex(arr1,0)) 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,0)),0) as A, gmaxs(complex(arr1,0)) 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,0)),0) as A, gmaxs(complex(arr1,0)) 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,0)),0) as A, gsums(complex(arr1,0)) 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,0)),0) as A, gproducts(complex(arr1,0)) 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,0)),0) as A, gsumsqrs(complex(arr1,0)) 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,0)),0) as A, gmeans(complex(arr1,0)) 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,0)),0) as A, gvariances(complex(arr1,0)) as B from tTableGramGroupAggrAll_tmp.tab having not all(A~=B) Caught an exception: Error in TaQL command: 'using style python select variances(gaggr(complex(arr1,0)),0) as A, gvariances(complex(arr1,0)) as B from tTableGramGroupAggrAll_tmp.tab having not all(A~=B)' Error in select expression: Erroneous use of function variances - invalid operand data type; function argument is not real using style python select stddevs(gaggr(complex(arr1,0)),0) as A, gstddevs(complex(arr1,0)) as B from tTableGramGroupAggrAll_tmp.tab having not all(A~=B) Caught an exception: Error in TaQL command: 'using style python select stddevs(gaggr(complex(arr1,0)),0) as A, gstddevs(complex(arr1,0)) as B from tTableGramGroupAggrAll_tmp.tab having not all(A~=B)' Error in select expression: Erroneous use of function stddevs - invalid operand data type; function argument is not real using style python select rmss(gaggr(complex(arr1,0)),0) as A, grmss(complex(arr1,0)) 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,0)),0) as A, grmss(complex(arr1,0)) 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-2.4.1/tables/TaQL/test/tTableGramGroupAggrAll.run000066400000000000000000000052511321422335000233450ustar00rootroot00000000000000#!/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,0)' do # Check gsum against sum, etc. for FUNC in min max sum product sumsqr mean variance stddev 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,0)' do for FUNC in min max sum product sumsqr mean variance stddev 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-2.4.1/tables/TaQL/test/tTableGramGroupAggrMaskAll.out000066400000000000000000000420611321422335000241640ustar00rootroot00000000000000*** 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 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 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 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 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 Caught an exception: Error in TaQL command: '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' Error in select expression: Erroneous use of function variance - invalid operand data type; function argument is not real 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 Caught an exception: Error in TaQL command: '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' Error in select expression: Erroneous use of function stddev - invalid operand data type; function argument is not real 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 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 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 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 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) Caught an exception: Error in TaQL command: '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)' Error in select expression: Erroneous use of function variances - invalid operand data type; function argument is not real 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) Caught an exception: Error in TaQL command: '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)' Error in select expression: Erroneous use of function stddevs - invalid operand data type; function argument is not real 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-2.4.1/tables/TaQL/test/tTableGramGroupAggrMaskAll.run000066400000000000000000000055641321422335000241700ustar00rootroot00000000000000#!/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 stddev 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 stddev 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-2.4.1/tables/TaQL/test/tTableGramMasked.out000066400000000000000000000113641321422335000222300ustar00rootroot00000000000000 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-2.4.1/tables/TaQL/test/tTableGramMasked.run000066400000000000000000000051751321422335000222300ustar00rootroot00000000000000#!/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 '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 '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 '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 '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 'select * from tTableGramMasked_tmp.tab1' # Remove the symlink rm -f tTableGramMasked casacore-2.4.1/tables/TaQL/test/tTableGramNull.out000066400000000000000000000025271321422335000217370ustar00rootroot00000000000000CALC 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-2.4.1/tables/TaQL/test/tTableGramNull.run000066400000000000000000000033041321422335000217260ustar00rootroot00000000000000#!/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 '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-2.4.1/tables/TaQL/test/tTableGramUpdate.out000066400000000000000000000641461321422335000222540ustar00rootroot00000000000000using 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])' 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-2.4.1/tables/TaQL/test/tTableGramUpdate.run000066400000000000000000000265261321422335000222510ustar00rootroot00000000000000#!/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 -pc '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 -pc '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 -pc '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 -pc '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 -pc '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 "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 "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 "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 "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 "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 -pc '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 -pc '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 "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 "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 -pc '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 -pc '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 "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 "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-2.4.1/tables/TaQL/test/ttaql.in000066400000000000000000000001651321422335000200020ustar00rootroot00000000000000# comment1 sum([1,2, # comment2 3, 4]) ; # 3+4; 'ab"# '" cd;e'f" ; 8 ; ; ;;;; 7 ; 1;2;3;4; 5+6 ; 18#1 casacore-2.4.1/tables/TaQL/test/ttaql.run000066400000000000000000000003641321422335000202010ustar00rootroot00000000000000#!/bin/sh # This script tests the taql program. # Direct command. ../../apps/taql '1+2' echo # Commands in 'interactive' mode. ../../apps/taql < #include #include #include #include #include //# table access #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 // // // Tables are 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. // // // // 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; or //
          //
        • 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, 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 (thus the endian format of the // machine being used). //
        • 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 three 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. //
        // 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. In the future this might be replaced by a proper B+-tree index // mechanism. // //

        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. // //

        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); // // //

        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 type (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. //
          An engine must be used to store 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. //
        // // 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 the table system 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. The Table system 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 the Table System. //
        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 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

        // // The Table System 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 the Table System. //
        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-2.4.1/tables/Tables/000077500000000000000000000000001321422335000157555ustar00rootroot00000000000000casacore-2.4.1/tables/Tables/ArrColData.h000066400000000000000000000262611321422335000201110ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef TABLES_ARRCOLDATA_H #define TABLES_ARRCOLDATA_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class ColumnSet; template class ArrayColumnDesc; 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. //
        // //
      • Default constructor //
      • Copy constructor //
      • Assignment operator // // //# A List of bugs, limitations, extensions or planned refinements. //
      • support tiling // template class ArrayColumnData : public PlainColumn { public: // Construct an array column object from the given description // in the given column set. // This constructor is used by ArrayColumnDesc::makeColumn. ArrayColumnData (const ArrayColumnDesc*, ColumnSet*); ~ArrayColumnData(); // Ask the data manager if the shape of an existing array can be changed. virtual Bool canChangeShape() const; // Ask if the data manager can handle a cell slice. virtual Bool canAccessSlice (Bool& reask) const; // Ask if the data manager can handle a column. virtual Bool canAccessArrayColumn (Bool& reask) const; // Ask if the data manager can handle some cells in a column. virtual Bool canAccessArrayColumnCells (Bool& reask) const; // Ask if the data manager can handle a column slice. virtual Bool canAccessColumnSlice (Bool& reask) const; // Initialize the rows from startRownr till endRownr (inclusive) // with the default value defined in the column description (if defined). void initialize (uInt startRownr, uInt 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 (uInt 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(uInt 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(uInt rownr) const; // Set dimensions of array in a particular cell. // void setShape (uInt rownr, const IPosition& shape); // The shape of tiles in the array can also be defined. void setShape (uInt rownr, const IPosition& shape, const IPosition& tileShape); // // Test if the given cell contains an array. Bool isDefined (uInt rownr) const; // Get the array from a particular cell. // The length of the buffer pointed to by arrayPtr must match // the actual length. This is checked by ArrayColumn. void get (uInt rownr, void* arrayPtr) const; // Get a slice of an N-dimensional array in a particular cell. // The length of the buffer pointed to by arrayPtr must match // the actual length. This is checked by ArrayColumn. void getSlice (uInt rownr, const Slicer&, void* 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 buffer pointed to by arrayPtr must match // the actual length. This is checked by ArrayColumn. void getArrayColumn (void* 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 buffer pointed to by arrayPtr must match // the actual length. This is checked by ArrayColumn. void getArrayColumnCells (const RefRows& rownrs, void* 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 buffer pointed to by arrayPtr must match // the actual length. This is checked by ArrayColumn. void getColumnSlice (const Slicer&, void* 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 buffer pointed to by arrayPtr must match // the actual length. This is checked by ArrayColumn. void getColumnSliceCells (const RefRows& rownrs, const Slicer&, void* arrayPtr) const; // Put the value in a particular cell. // The length of the buffer pointed to by arrayPtr must match // the actual length. This is checked by ArrayColumn. void put (uInt rownr, const void* arrayPtr); // Put a slice of an N-dimensional array in a particular cell. // The length of the buffer pointed to by arrayPtr must match // the actual length. This is checked by ArrayColumn. void putSlice (uInt rownr, const Slicer&, const void* 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 buffer pointed to by arrayPtr must match // the actual length. This is checked by ArrayColumn. void putArrayColumn (const void* 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 buffer pointed to by arrayPtr must match // the actual length. This is checked by ArrayColumn. void putArrayColumnCells (const RefRows& rownrs, const void* 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 buffer pointed to by arrayPtr must match // the actual length. This is checked by ArrayColumn. void putColumnSlice (const Slicer&, const void* 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 buffer pointed to by arrayPtr must match // the actual length. This is checked by ArrayColumn. void putColumnSliceCells (const RefRows& rownrs, const Slicer&, const void* arrayPtr); // Create a data manager column object for this column. void createDataManagerColumn(); private: // Pointer to column description. const ArrayColumnDesc* arrDescPtr_p; // Is the shape for all arrays in the columns defined. Bool shapeColDef_p; // Shape for all arrays in the column. IPosition shapeCol_p; // Copy constructor cannot be used. ArrayColumnData (const ArrayColumnData&); // Assignment cannot be used. ArrayColumnData& operator= (const ArrayColumnData&); // 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 #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-2.4.1/tables/Tables/ArrColData.tcc000066400000000000000000000314131321422335000204260ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef TABLES_ARRCOLDATA_TCC #define TABLES_ARRCOLDATA_TCC #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template ArrayColumnData::ArrayColumnData (const ArrayColumnDesc* cd, ColumnSet* csp) : PlainColumn (cd, csp), arrDescPtr_p (cd), shapeColDef_p(False), shapeCol_p () { if (cd->shape().nelements() > 0) { setShapeColumn (cd->shape()); } } template ArrayColumnData::~ArrayColumnData() {} //# Create the data manager column for this column. //# The shape of a fixed shape array has to be defined in advance. template 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. template void ArrayColumnData::initialize (uInt, uInt) {} template uInt ArrayColumnData::ndimColumn() const { Int ndim = columnDesc().ndim(); return (ndim > 0 ? ndim : shapeCol_p.nelements()); } template IPosition ArrayColumnData::shapeColumn() const { return shapeCol_p; } template 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; } template Bool ArrayColumnData::isDefined (uInt rownr) const { return dataColPtr_p->isShapeDefined(rownr); } template uInt ArrayColumnData::ndim (uInt rownr) const { return dataColPtr_p->ndim(rownr); } template IPosition ArrayColumnData::shape (uInt rownr) const { return dataColPtr_p->shape(rownr); } template IPosition ArrayColumnData::tileShape (uInt rownr) const { return dataColPtr_p->tileShape(rownr); } template void ArrayColumnData::setShape (uInt rownr, const IPosition& shp) { checkShape (shp); checkWriteLock (True); dataColPtr_p->setShape (rownr, shp); autoReleaseLock(); } template void ArrayColumnData::setShape (uInt rownr, const IPosition& shp, const IPosition& tileShp) { checkShape (shp); checkWriteLock (True); dataColPtr_p->setShapeTiled (rownr, shp, tileShp); autoReleaseLock(); } template Bool ArrayColumnData::canChangeShape() const { return dataColPtr_p->canChangeShape(); } template Bool ArrayColumnData::canAccessSlice (Bool& reask) const { return dataColPtr_p->canAccessSlice (reask); } template Bool ArrayColumnData::canAccessArrayColumn (Bool& reask) const { return dataColPtr_p->canAccessArrayColumn (reask); } template Bool ArrayColumnData::canAccessArrayColumnCells (Bool& reask) const { return dataColPtr_p->canAccessArrayColumnCells (reask); } template Bool ArrayColumnData::canAccessColumnSlice (Bool& reask) const { return dataColPtr_p->canAccessColumnSlice (reask); } template void ArrayColumnData::get (uInt rownr, void* arrayPtr) const { if (rtraceColumn_p) { TableTrace::trace (traceId(), columnDesc().name(), 'r', rownr, static_cast*>(arrayPtr)->shape()); } checkReadLock (True); dataColPtr_p->getArrayV (rownr, (Array*)arrayPtr); autoReleaseLock(); } template void ArrayColumnData::getSlice (uInt rownr, const Slicer& ns, void* arrayPtr) const { if (rtraceColumn_p) { TableTrace::trace (traceId(), columnDesc().name(), 'r', rownr, static_cast*>(arrayPtr)->shape(), ns.start(), ns.end(), ns.stride()); } checkReadLock (True); dataColPtr_p->getSliceV (rownr, ns, (Array*)arrayPtr); autoReleaseLock(); } template void ArrayColumnData::put (uInt rownr, const void* arrayPtr) { if (wtraceColumn_p) { TableTrace::trace (traceId(), columnDesc().name(), 'w', rownr, static_cast*>(arrayPtr)->shape()); } checkValueLength ((const Array*)arrayPtr); checkWriteLock (True); dataColPtr_p->putArrayV (rownr, (const Array*)arrayPtr); autoReleaseLock(); } template void ArrayColumnData::putSlice (uInt rownr, const Slicer& ns, const void* arrayPtr) { if (wtraceColumn_p) { TableTrace::trace (traceId(), columnDesc().name(), 'w', rownr, static_cast*>(arrayPtr)->shape(), ns.start(), ns.end(), ns.stride()); } checkValueLength ((const Array*)arrayPtr); checkWriteLock (True); dataColPtr_p->putSliceV (rownr, ns, (const Array*)arrayPtr); autoReleaseLock(); } //# Get or put the column by iterating through the array and getting the //# column array for each row. template void ArrayColumnData::getArrayColumn (void* arrayPtr) const { if (rtraceColumn_p) { TableTrace::trace (traceId(), columnDesc().name(), 'r', static_cast*>(arrayPtr)->shape()); } checkReadLock (True); dataColPtr_p->getArrayColumnV ((Array*)arrayPtr); autoReleaseLock(); } template void ArrayColumnData::getArrayColumnCells (const RefRows& rownrs, void *arrayPtr) const { if (rtraceColumn_p) { TableTrace::trace (traceId(), columnDesc().name(), 'r', rownrs, static_cast*>(arrayPtr)->shape()); } checkReadLock (True); dataColPtr_p->getArrayColumnCellsV (rownrs, arrayPtr); autoReleaseLock(); } template void ArrayColumnData::getColumnSlice (const Slicer& ns, void* arrayPtr) const { if (rtraceColumn_p) { TableTrace::trace (traceId(), columnDesc().name(), 'r', static_cast*>(arrayPtr)->shape(), ns.start(), ns.end(), ns.stride()); } checkReadLock (True); dataColPtr_p->getColumnSliceV (ns, (Array*)arrayPtr); autoReleaseLock(); } template void ArrayColumnData::getColumnSliceCells (const RefRows& rownrs, const Slicer& ns, void* arrayPtr) const { if (rtraceColumn_p) { TableTrace::trace (traceId(), columnDesc().name(), 'r', rownrs, static_cast*>(arrayPtr)->shape(), ns.start(), ns.end(), ns.stride()); } checkReadLock (True); dataColPtr_p->getColumnSliceCellsV (rownrs, ns, arrayPtr); autoReleaseLock(); } template void ArrayColumnData::putArrayColumn (const void* arrayPtr) { if (wtraceColumn_p) { TableTrace::trace (traceId(), columnDesc().name(), 'w', static_cast*>(arrayPtr)->shape()); } checkValueLength ((const Array*)arrayPtr); checkWriteLock (True); dataColPtr_p->putArrayColumnV ((const Array*)arrayPtr); autoReleaseLock(); } template void ArrayColumnData::putArrayColumnCells (const RefRows& rownrs, const void* arrayPtr) { if (wtraceColumn_p) { TableTrace::trace (traceId(), columnDesc().name(), 'w', rownrs, static_cast*>(arrayPtr)->shape()); } checkValueLength ((const Array*)arrayPtr); checkWriteLock (True); dataColPtr_p->putArrayColumnCellsV (rownrs, arrayPtr); autoReleaseLock(); } template void ArrayColumnData::putColumnSlice (const Slicer& ns, const void* arrayPtr) { if (wtraceColumn_p) { TableTrace::trace (traceId(), columnDesc().name(), 'w', static_cast*>(arrayPtr)->shape(), ns.start(), ns.end(), ns.stride()); } checkValueLength ((const Array*)arrayPtr); checkWriteLock (True); dataColPtr_p->putColumnSliceV (ns, (const Array*)arrayPtr); autoReleaseLock(); } template void ArrayColumnData::putColumnSliceCells (const RefRows& rownrs, const Slicer& ns, const void* arrayPtr) { if (wtraceColumn_p) { TableTrace::trace (traceId(), columnDesc().name(), 'w', rownrs, static_cast*>(arrayPtr)->shape(), ns.start(), ns.end(), ns.stride()); } checkValueLength ((const Array*)arrayPtr); checkWriteLock (True); dataColPtr_p->putColumnSliceCellsV (rownrs, ns, arrayPtr); 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. template 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". template void ArrayColumnData::putFileDerived (AipsIO& ios) { ios << (uInt)1; // class version 1 ios << dataManPtr_p->sequenceNr(); ios << shapeColDef_p; if (shapeColDef_p) { ios << shapeCol_p; } } template 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 #endif casacore-2.4.1/tables/Tables/ArrColDesc.h000066400000000000000000000305121321422335000201100ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef TABLES_ARRCOLDESC_H #define TABLES_ARRCOLDESC_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class PlainColumn; class ColumnSet; template class Array; // // Templated class for description of table array columns // // // // // //
      • BaseColumnDesc (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, 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 BaseColumnDesc, 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 BaseColumnDesc { 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; // 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; // 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); protected: // Put the object. virtual void putDesc (AipsIO&) const; // Get the object. virtual void getDesc (AipsIO&); }; //# Explicitly instantiate these templates in ArrColDesc_tmpl.cc #ifdef AIPS_CXX11 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; #endif } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-2.4.1/tables/Tables/ArrColDesc.tcc000066400000000000000000000154151321422335000204370ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef TABLES_ARRCOLDESC_TCC #define TABLES_ARRCOLDESC_TCC #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template ArrayColumnDesc::ArrayColumnDesc (const String& name, Int ndim, int opt) : BaseColumnDesc (name, "", "", "", ValType::getType(static_cast(0)), valDataTypeId(static_cast(0)), opt, ndim, IPosition(), False, True, False) { if (nrdim_p <= 0) { nrdim_p = -1; } } template ArrayColumnDesc::ArrayColumnDesc (const String& name, const String& comment, Int ndim, int opt) : BaseColumnDesc (name, comment, "", "", ValType::getType(static_cast(0)), valDataTypeId(static_cast(0)), opt, ndim, IPosition(), False, True, False) { if (nrdim_p == 0) { nrdim_p = -1; } } template ArrayColumnDesc::ArrayColumnDesc (const String& name, const String& comment, const String& dataManName, const String& dataManGroup, Int ndim, int opt) : BaseColumnDesc (name, comment, dataManName, dataManGroup, ValType::getType(static_cast(0)), valDataTypeId(static_cast(0)), opt, ndim, IPosition(), False, True, False) { if (nrdim_p == 0) { nrdim_p = -1; } } template ArrayColumnDesc::ArrayColumnDesc (const String& name, const IPosition& shp, int opt) : BaseColumnDesc (name, "", "", "", ValType::getType(static_cast(0)), valDataTypeId(static_cast(0)), opt, shp.nelements(), shp, False, True, False) { if (nrdim_p == 0) { nrdim_p = -1; } } template ArrayColumnDesc::ArrayColumnDesc (const String& name, const String& comment, const IPosition& shp, int opt) : BaseColumnDesc (name, comment, "", "", ValType::getType(static_cast(0)), valDataTypeId(static_cast(0)), opt, shp.nelements(), shp, False, True, False) { if (nrdim_p == 0) { nrdim_p = -1; } } template ArrayColumnDesc::ArrayColumnDesc (const String& name, const String& comment, const String& dataManName, const String& dataManGroup, const IPosition& shp, int opt, int ndim) : BaseColumnDesc (name, comment, dataManName, dataManGroup, ValType::getType(static_cast(0)), valDataTypeId(static_cast(0)), opt, shp.nelements(), shp, False, True, False) { if (nrdim_p == 0) { nrdim_p = -1; } 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) : BaseColumnDesc (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) { BaseColumnDesc::operator= (that); return *this; } //# Clone this column description to another. template BaseColumnDesc* ArrayColumnDesc::clone() const { BaseColumnDesc* ptr = new ArrayColumnDesc(*this); return ptr; } //# Return the class name. template String ArrayColumnDesc::className() const { return "ArrayColumnDesc<" + dataTypeId(); } //# Register the makeDesc function. template void ArrayColumnDesc::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 ArrayColumnDesc::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; } template void ArrayColumnDesc::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. template void ArrayColumnDesc::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. template PlainColumn* ArrayColumnDesc::makeColumn (ColumnSet* csp) const { PlainColumn* bcp = new ArrayColumnData (this, csp); return bcp; } } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/tables/Tables/ArrColDesc_tmpl.cc000066400000000000000000000036041321422335000213040ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: Vector.h 21545 2015-01-22 19:36:35Z gervandiepen $ //# Includes #include //# Instantiate extern templates for often used types. #ifdef AIPS_CXX11 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; } #endif casacore-2.4.1/tables/Tables/ArrayColumn.h000066400000000000000000000632521321422335000203720ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: ArrayColumn.h 21521 2014-12-10 08:06:42Z gervandiepen $ #ifndef TABLES_ARRAYCOLUMN_H #define TABLES_ARRAYCOLUMN_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class RefRows; template class Array; template class BaseSlicesFunctor; class IPosition; class Slice; class Slicer; class ColumnSlicer; 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. // // // 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 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 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 (uInt 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 (uInt 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 (uInt rownr, Array& array, Bool resize = False) const; Array get (uInt rownr) const; Array operator() (uInt 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 (uInt rownr, const Slicer& arraySection, Array& array, Bool resize = False) const; Array getSlice (uInt 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 (uInt rownr, const Vector >& arraySlices, Array& arr, Bool resize = False) const; Array getSlice (uInt 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; // The get() function like above which does not check shapes, etc. // It is faster and can be used for performance reasons if one // knows for sure that the arguments are correct. // E.g. it is used internally in virtual column engines. void baseGet (uInt rownr, Array& array) const { baseColPtr_p->get (rownr, &array); } // 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 (uInt rownr, const IPosition& shape); // Try to store the array in a tiled way using the given tile shape. void setShape (uInt 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 (uInt 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 (uInt 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 (uInt thisRownr, const TableColumn& that, uInt thatRownr, Bool preserveTileShape=False); // // 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 (uInt rownr, const Slicer& arraySection, const Array& array); void putSlice (uInt 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); // The put() function like above which does not check shapes, etc. // It is faster and can be used for performance reasons if one // knows for sure that the arguments are correct. // E.g. it is used internally in virtual column engines. void basePut (uInt rownr, const Array& array) { baseColPtr_p->put (rownr, &array); } private: // Check if the data type matches the column data type. void checkDataType() const; // Check the shape of the array. If the array is empty or if // resize=True, the array is resized if needed. // An exception is thrown if not conforming. void checkShape (const IPosition& shp, Array& arr, Bool resize, const String& where) const; void checkShape (const IPosition& shp, Array& arr, Bool resize, const char * where) const; protected: // 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, IPosition& arrEnd, Array& array) const; // Keep switches to determine if a slice or an entire column can // be accessed or the change of an array can be changed. // True = yes; False = no. mutable Bool canAccessSlice_p; mutable Bool canAccessColumn_p; mutable Bool canAccessColumnSlice_p; // Keep switches to know if access knowledge is permanent or has // to be asked again the next time. mutable Bool reaskAccessSlice_p; mutable Bool reaskAccessColumn_p; mutable Bool reaskAccessColumnSlice_p; }; class ColumnSlicer { public: // Create a ColumnSlicer for use in one of the overloads of ArrayColumn::getColumnCells. That method // takes a potentially complex select 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 represnted // as a set of source,destination slicers where one is applied to the cell and the other to the // destination array. // // The shape paramter is the shape of the destination excluding the row axis. // // // // The Slicer objects provided (by pointer) will be owned by the ColumnSlicer object which will // delete them in its destructor. ColumnSlicer (const IPosition & shape, Vector dataSlicers, 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 (String ("ColumnSlicer (ctor):: ") + message); } } // Kill off the Slicer objects. ~ColumnSlicer (){ freeSlicers(); } // Accessor that returns the dataSlicers. const Vector & getDataSlicers () const { return dataSlicers_p; } // Accessor that returns the desintation slicers const Vector & getDestinationSlicers () const { return destinationSlicers_p; } // Accessor that returns the shape. const IPosition & shape () const { return shape_p; } private: void 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 validateParameters () { // 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(); } Vector dataSlicers_p; Vector destinationSlicers_p; IPosition shape_p; }; //# Explicitly instantiate these templates in ArrayColumn_tmpl.cc #ifdef AIPS_CXX11 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; #endif } //# 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-2.4.1/tables/Tables/ArrayColumn.tcc000066400000000000000000001123631321422335000207120ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: ArrayColumn.tcc 21562 2015-02-16 07:03:44Z gervandiepen $ #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() : TableColumn (), canAccessSlice_p (False), canAccessColumn_p (False), canAccessColumnSlice_p (False), reaskAccessSlice_p (True), reaskAccessColumn_p (True), reaskAccessColumnSlice_p (True) {} template ArrayColumn::ArrayColumn (const Table& tab, const String& columnName) : TableColumn (tab, columnName), canAccessSlice_p (False), canAccessColumn_p (False), canAccessColumnSlice_p (False), reaskAccessSlice_p (True), reaskAccessColumn_p (True), reaskAccessColumnSlice_p (True) { checkDataType(); } template ArrayColumn::ArrayColumn (const TableColumn& column) : TableColumn (column), canAccessSlice_p (False), canAccessColumn_p (False), canAccessColumnSlice_p (False), reaskAccessSlice_p (True), reaskAccessColumn_p (True), reaskAccessColumnSlice_p (True) { checkDataType(); } template ArrayColumn::ArrayColumn (const ArrayColumn& that) : TableColumn (that), canAccessSlice_p (that.canAccessSlice_p), canAccessColumn_p (that.canAccessColumn_p), canAccessColumnSlice_p (that.canAccessColumnSlice_p), reaskAccessSlice_p (that.reaskAccessSlice_p), reaskAccessColumn_p (that.reaskAccessColumn_p), reaskAccessColumnSlice_p (that.reaskAccessColumnSlice_p) {} 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); canAccessSlice_p = that.canAccessSlice_p; canAccessColumn_p = that.canAccessColumn_p; canAccessColumnSlice_p = that.canAccessColumnSlice_p; reaskAccessSlice_p = that.reaskAccessSlice_p; reaskAccessColumn_p = that.reaskAccessColumn_p; reaskAccessColumnSlice_p = that.reaskAccessColumnSlice_p; } 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 void ArrayColumn::checkShape (const IPosition& shp, Array& arr, Bool resize, const char * where) const { if (! shp.isEqual (arr.shape())) { if (resize || arr.nelements() == 0) { arr.resize (shp); } else { throw TableArrayConformanceError(where); } } } template void ArrayColumn::checkShape (const IPosition& shp, Array& arr, Bool resize, const String& where) const { checkShape (shp, arr, resize, where.c_str()); } template Array ArrayColumn::operator() (uInt rownr) const { Array arr; get (rownr, arr); return arr; } template Array ArrayColumn::get (uInt rownr) const { Array arr; get (rownr, arr); return arr; } template void ArrayColumn::get (uInt rownr, Array& arr, Bool resize) const { TABLECOLUMNCHECKROW(rownr); // Check array conformance and resize if needed and possible. checkShape (shape(rownr), arr, resize, "ArrayColumn::get"); baseColPtr_p->get (rownr, &arr); } template Array ArrayColumn::getSlice (uInt rownr, const Slicer& arraySection) const { Array arr; getSlice (rownr, arraySection, arr); return arr; } template void ArrayColumn::getSlice (uInt rownr, const Slicer& arraySection, Array& 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); checkShape (shp, arr, resize, "ArrayColumn::getSlice"); //# Ask if we can access the slice (if that is not known yet). if (reaskAccessSlice_p) { canAccessSlice_p = baseColPtr_p->canAccessSlice (reaskAccessSlice_p); } //# Access the slice if possible. //# Otherwise get the entire array and slice it. if (canAccessSlice_p) { //# 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); } }else{ Array array(arrayShape); baseColPtr_p->get (rownr, &array); arr = array(blc, trc, inc); } } template Array ArrayColumn::getSlice (uInt rownr, const Vector >& arraySlices) const { Array arr; getSlice (rownr, arraySlices, arr); return arr; } template void ArrayColumn::getSlice (uInt rownr, const Vector >& arraySlices, Array& arr, Bool resize) const { TABLECOLUMNCHECKROW(rownr); // Use shape of row. IPosition colShp = shape(rownr); Vector > slices(arraySlices); Slicer slicer; IPosition shp = Slice::checkSlices (slices, slicer, colShp); // Check array conformance and resize if needed and possible. checkShape (shp, arr, resize, "ArrayColumn::getSlice"); // Now loop through all the slices and fill the array in parts. IPosition arrEnd (slicer.length() - 1); GetCellSlices functor(*this, rownr); handleSlices (slices, functor, slicer, arrEnd, arr); } template void ArrayColumn::getColumnCells (const RefRows & rows, const ColumnSlicer & columnSlicer, Array& 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 destinationSlicers = columnSlicer.getDestinationSlicers(); IPosition destinationShape (columnSlicer.shape()); destinationShape.append (IPosition (1, rows.nrows())); checkShape (destinationShape, destination, resize, "ArrayColumn::getColumnCells (rows, slicers, ...)"); // Fill the destination array one row at a time. // If rows is not sliced then rowNumbers is simply a vector of the relevant // row numbers. When sliced, rowNumbers is a triple: (start, nRows, increment). // Thus we have two different ways of walking through the selected rows. const Vector & rowNumbers = rows.rowVector(); Bool useRowSlicing = rows.isSliced(); Int row = 0; Int increment = 1; if (useRowSlicing){ AlwaysAssert (rowNumbers.nelements() == 3, AipsError); increment = rowNumbers [2]; row = rowNumbers [0] ; } uInt nSlicers = dataSlicers.size(); uInt nRows = rows.nrows(); for (uInt i = 0; i < nRows; i++){ // Create a section of the destination array that will hold the // data for this row ([s1, ...,sN, nR] --> [s1,...,sN]. Array destinationRow = destination [i]; for (uInt j = 0; j < nSlicers; j++){ Array destinationRowSection = destinationRow (* destinationSlicers[j]); baseColPtr_p->getSlice (row, * dataSlicers[j], & destinationRowSection); } row = useRowSlicing ? row + increment : rowNumbers (i); } } //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 void ArrayColumn::handleSlices (const Vector >& slices, BaseSlicesFunctor& functor, const Slicer& slicer, IPosition& arrEnd, Array& arr) const { uInt nrdim = slicer.ndim(); IPosition arrStart (arrEnd.size(), 0); // for getColumn arrEnd.size() > nrdim IPosition colStart (slicer.start()); IPosition colLen (slicer.length()); IPosition colIncr (slicer.stride()); IPosition pos(nrdim, 0); while (True) { Array refArr (arr(arrStart, arrEnd)); functor.apply (Slicer(colStart, colLen, colIncr), refArr); uInt i; for (i=0; i Array ArrayColumn::getColumn() const { Array arr; getColumn (arr); return arr; } template void ArrayColumn::getColumn (Array& arr, Bool resize) const { uInt 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. checkShape (shp, arr, resize, "ArrayColumn::getColumn"); if (!arr.empty()) { //# Ask if we can access the column (if that is not known yet). if (reaskAccessColumn_p) { canAccessColumn_p = baseColPtr_p->canAccessArrayColumn (reaskAccessColumn_p); } //# Access the column if possible. //# Otherwise fill the entire array by looping through all cells. if (canAccessColumn_p) { baseColPtr_p->getArrayColumn (&arr); }else{ ArrayIterator iter(arr, arr.ndim()-1); for (uInt rownr=0; rownr& darr = iter.array(); if (! darr.shape().isEqual (baseColPtr_p->shape (rownr))) { throw TableArrayConformanceError ("ArrayColumn::getColumn cannot be done for column " + baseColPtr_p->columnDesc().name() + "; the array shapes vary"); } baseColPtr_p->get (rownr, &darr); iter.next(); } } } } 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 { uInt 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. checkShape (shp, arr, resize, "ArrayColumn::getColumn"); if (!arr.empty()) { //# Ask if we can access the column slice (if that is not known yet). if (reaskAccessColumnSlice_p) { canAccessColumnSlice_p = baseColPtr_p->canAccessColumnSlice (reaskAccessColumnSlice_p); } //# Access the column slice if possible. //# Otherwise fill the entire array by looping through all cells. Slicer defSlicer (blc, trc, inc, Slicer::endIsLast); if (canAccessColumnSlice_p) { baseColPtr_p->getColumnSlice (defSlicer, &arr); }else{ ArrayIterator iter(arr, arr.ndim()-1); for (uInt rownr=0; rownr 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 { uInt 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. checkShape (shp, arr, resize, "ArrayColumn::getColumn"); // Now loop through all the slices and fill the array in parts. IPosition arrEnd (slicer.length() - 1); arrEnd.append (IPosition(1,nrrow-1)); GetColumnSlices functor(*this); this->handleSlices (slices, functor, slicer, arrEnd, arr); } 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 { uInt 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) { getColumn (arr, resize); } else { getColumnCells (RefRows(blc(0), trc(0), inc(0)), 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 { uInt 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. checkShape (arrshp, arr, resize, "ArrayColumn::getColumnCells"); baseColPtr_p->getArrayColumnCells (rownrs, &arr); } 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 { uInt 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) { getColumn (arraySection, arr, resize); } else { getColumnCells (RefRows(blc(0), trc(0), inc(0)), 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 { uInt 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. checkShape (arrshp, arr, resize, "ArrayColumn::getColumnCells"); if (!arr.empty()) { //# Ask if we can access the column slice (if that is not known yet). if (reaskAccessColumnSlice_p) { canAccessColumnSlice_p = baseColPtr_p->canAccessColumnSlice (reaskAccessColumnSlice_p); } //# Access the column slice if possible. //# Otherwise fill the entire array by looping through all cells. Slicer defSlicer (arrblc, arrtrc, arrinc, Slicer::endIsLast); if (canAccessColumnSlice_p) { baseColPtr_p->getColumnSliceCells (rownrs, defSlicer, &arr); } else { ArrayIterator iter(arr, arr.ndim()-1); RefRowsSliceIter rowsIter(rownrs); while (! rowsIter.pastEnd()) { uInt rownr = rowsIter.sliceStart(); uInt end = rowsIter.sliceEnd(); uInt incr = rowsIter.sliceIncr(); while (rownr <= end) { getSlice (rownr, defSlicer, iter.array()); iter.next(); rownr += incr; } rowsIter++; } } } } template void ArrayColumn::setShape (uInt 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) + " column " + baseColPtr_p->columnDesc().name())); } } } template void ArrayColumn::setShape (uInt 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())); } } } template void ArrayColumn::put (uInt rownr, const Array& 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 (! arr.shape().isEqual (baseColPtr_p->shape (rownr))) { if (!canChangeShape_p) { throw (TableArrayConformanceError ("ArrayColumn::put for row " + String::toString(rownr) + " in column " + baseColPtr_p->columnDesc().name())); } baseColPtr_p->setShape (rownr, arr.shape()); } } baseColPtr_p->put (rownr, &arr); } template void ArrayColumn::putSlice (uInt rownr, const Slicer& arraySection, const Array& arr) { checkWritable(); TABLECOLUMNCHECKROW(rownr); //# Check the array conformance. IPosition arrayShape (shape(rownr)); IPosition blc,trc,inc; IPosition shp = arraySection.inferShapeFromSource (arrayShape, blc,trc,inc); if (! shp.isEqual (arr.shape())) { throw (TableArrayConformanceError ("ArrayColumn::putSlice for row " + String::toString(rownr) + " in column " + baseColPtr_p->columnDesc().name())); } //# Ask if we can access the slice (if that is not known yet). if (reaskAccessSlice_p) { canAccessSlice_p = baseColPtr_p->canAccessSlice (reaskAccessSlice_p); } //# Access the slice if possible. //# Otherwise get the entire array, put the slice and put it back. if (canAccessSlice_p) { baseColPtr_p->putSlice (rownr, arraySection, &arr); }else{ Array array(arrayShape); baseColPtr_p->get (rownr, &array); array(blc, trc, inc) = arr; baseColPtr_p->put (rownr, &array); } } template void ArrayColumn::putSlice (uInt rownr, const Vector >& arraySlices, const Array& 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); if (! shp.isEqual (arr.shape())) { throw (TableArrayConformanceError ("ArrayColumn::putSlice for row " + String::toString(rownr) + " in column " + baseColPtr_p->columnDesc().name())); } // Now loop through all the slices and fill the array in parts. IPosition arrEnd (slicer.length() - 1); PutCellSlices functor(*this, rownr); Array arrc(arr); // make non-const this->handleSlices (slices, functor, slicer, arrEnd, arrc); } template void ArrayColumn::putColumnCells (const RefRows & rows, const Vector >& arraySlices, const Array& source) { checkWritable(); // Empty the destination array one row at a time. const Vector& rowNumbers = rows.rowVector(); // If rows is not sliced then rowNumbers is simply a vector of the relevant // row numbers. When sliced, rowNumbers is a triple: (start, nRows, increment). // Thus we have two different ways of walking through the selected rows. Int row=0; Int increment=1; Bool useSlices = rows.isSliced(); if (useSlices){ AlwaysAssert (rowNumbers.nelements() == 3, AipsError); increment = rowNumbers [2]; row = rowNumbers [0] - increment; // allows increment before use inside loop } for (uInt i = 0; i < rows.nrows(); i++){ Array sourceSection = source [i]; // this row's data as array if (rows.isSliced()){ row += increment; } else{ row = rowNumbers (i); } putSlice (row, arraySlices, sourceSection); } } template void ArrayColumn::putColumnCells (const RefRows& rows, const ColumnSlicer & columnSlicer, const Array& source) { checkWritable(); // 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. Resize the source array to match. const Vector bufferSlicers = columnSlicer.getDataSlicers(); const Vector arraySlicers = columnSlicer.getDestinationSlicers(); IPosition sourceShape (columnSlicer.shape()); sourceShape.append (IPosition (1, rows.nrows())); ThrowIf (sourceShape != source.shape(), String::format ("putColumnCells: Expected array with shape %d but got %d", sourceShape.toString().c_str(), source.shape().toString().c_str())); // Fill the source array one row at a time. // If rows is not sliced then rowNumbers is simply a vector of the relevant // row numbers. When sliced, rowNumbers is a triple: (start, nRows, increment). // Thus we have two different ways of walking through the selected rows. const Vector & rowNumbers = rows.rowVector(); Bool useRowSlicing = rows.isSliced(); int row = 0; int increment = 1; if (useRowSlicing){ AlwaysAssert (rowNumbers.nelements() == 3, AipsError); increment = rowNumbers [2]; row = rowNumbers [0] ; } uInt nSlicers = bufferSlicers.size(); uInt nRows = rows.nrows(); for (uInt i = 0; i < nRows; i++){ // Create a section of the source array that will hold the // data for this row ([s1, ...,sN, nR] --> [s1,...,sN]. Array sourceRow = source [i]; for (uInt j = 0; j < nSlicers; j++){ Array sourceRowSection = sourceRow (* arraySlicers[j]); baseColPtr_p->putSlice (row, * bufferSlicers[j], & sourceRowSection); } row = useRowSlicing ? row + increment : rowNumbers (i); } } template void ArrayColumn::put (uInt thisRownr, const TableColumn& that, uInt thatRownr, Bool preserveTileShape) { TableColumn::put (thisRownr, that, thatRownr, preserveTileShape); } template void ArrayColumn::putColumn (const Array& arr) { checkWritable(); //# First check if number of rows matches. uInt nrrow = nrow(); IPosition shp = arr.shape(); uInt last = shp.nelements() - 1; if (shp(last) != Int(nrrow)) { throw (TableArrayConformanceError ("ArrayColumn::putColumn for column " + baseColPtr_p->columnDesc().name())); } //# 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) { if (! shp.isEqual (shapeColumn())) { throw (TableArrayConformanceError ("ArrayColumn::putColumn for column " + baseColPtr_p->columnDesc().name())); } }else{ //# Otherwise set the shape of each cell (as far as needed). for (uInt i=0; icanAccessArrayColumn (reaskAccessColumn_p); } //# Access the column if possible. //# Otherwise put the entire array by looping through all cells. if (canAccessColumn_p) { baseColPtr_p->putArrayColumn (&arr); }else{ if (arr.nelements() > 0) { ReadOnlyArrayIterator iter(arr, arr.ndim()-1); for (uInt rownr=0; rownrput (rownr, &(iter.array())); iter.next(); } } } } template void ArrayColumn::putColumn (const Slicer& arraySection, const Array& arr) { checkWritable(); uInt 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 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 blc,trc,inc; IPosition shp = arraySection.inferShapeFromSource (shapeColumn(), blc,trc,inc); if (! shp.isEqual(arrshp)) { throw (TableArrayConformanceError ("ArrayColumn::putColumn for column " + baseColPtr_p->columnDesc().name())); } } //# Ask if we can access the column slice (if that is not known yet). if (reaskAccessColumnSlice_p) { canAccessColumnSlice_p = baseColPtr_p->canAccessColumnSlice (reaskAccessColumnSlice_p); } //# Access the column slice if possible. //# Otherwise put the entire array by looping through all cells. if (canAccessColumnSlice_p) { baseColPtr_p->putColumnSlice (arraySection, &arr); }else{ if (arr.nelements() > 0) { ReadOnlyArrayIterator iter(arr, arr.ndim()-1); for (uInt rownr=0; rownr void ArrayColumn::putColumn (const Vector >& arraySlices, const Array& arr) { checkWritable(); uInt 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. IPosition arrEnd (slicer.length() - 1); arrEnd.append (IPosition(1,nrrow-1)); PutColumnSlices functor(*this); Array arrc(arr); // make non-const this->handleSlices (slices, functor, slicer, arrEnd, arrc); } template void ArrayColumn::putColumnRange (const Slicer& rowRange, const Array& arr) { uInt 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) { putColumn (arr); } else { putColumnCells (RefRows(blc(0), trc(0), inc(0)), arr); } } template void ArrayColumn::putColumnCells (const RefRows& rownrs, const Array& arr) { checkWritable(); //# First check if number of rows matches. uInt 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()) { uInt rownr = iter.sliceStart(); uInt end = iter.sliceEnd(); uInt incr = iter.sliceIncr(); while (rownr <= end) { setShape (rownr, arrshp); rownr += incr; } iter++; } } //# Put the entire array. baseColPtr_p->putArrayColumnCells (rownrs, &arr); } template void ArrayColumn::putColumnRange (const Slicer& rowRange, const Slicer& arraySection, const Array& arr) { uInt 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) { putColumn (arraySection, arr); } else { putColumnCells (RefRows(blc(0), trc(0), inc(0)), arraySection, arr); } } template void ArrayColumn::putColumnCells (const RefRows& rownrs, const Slicer& arraySection, const Array& arr) { checkWritable(); //# First check if number of rows matches. uInt 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); } //# 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) { uInt nrrow = nrow(); for (uInt i=0; i void ArrayColumn::putColumn (const ArrayColumn& that) { checkWritable(); //# Check the column lengths. uInt nrrow = nrow(); if (nrrow != that.nrow()) { throw (TableConformanceError ("Nr of rows differ in ArrayColumn::putColumn for column " + baseColPtr_p->columnDesc().name())); } for (uInt i=0; i #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. ArrayColumn::handleSlices is used to perform all common // operations using a functor derived from this base class. // template class BaseSlicesFunctor { public: virtual ~BaseSlicesFunctor() {} virtual void apply (const Slicer& slicer, Array& arr) = 0; }; // Functor to get irregular array slices from a cell template class GetCellSlices : public BaseSlicesFunctor { public: GetCellSlices (const ArrayColumn& col, uInt rownr) : itsCol(col), itsRow(rownr) {} virtual void apply (const Slicer& slicer, Array& arr) { itsCol.getSlice (itsRow, slicer, arr); } private: const ArrayColumn& itsCol; uInt itsRow; }; // Functor to get irregular array slices from a column template class GetColumnSlices : public BaseSlicesFunctor { public: GetColumnSlices (const ArrayColumn& col) : itsCol(col) {} virtual void apply (const Slicer& slicer, Array& arr) { itsCol.getColumn (slicer, arr); } private: const ArrayColumn& itsCol; }; // Functor to put irregular array slices into a cell template class PutCellSlices : public BaseSlicesFunctor { public: PutCellSlices (ArrayColumn& col, uInt rownr) : itsCol(col), itsRow(rownr) {} virtual void apply (const Slicer& slicer, Array& arr) { itsCol.putSlice (itsRow, slicer, arr); } private: ArrayColumn& itsCol; uInt itsRow; }; // Functor to get irregular array slices from a column template class PutColumnSlices : public BaseSlicesFunctor { public: PutColumnSlices (ArrayColumn& col) : itsCol(col) {} virtual void apply (const Slicer& slicer, Array& arr) { itsCol.putColumn (slicer, arr); } private: ArrayColumn& itsCol; }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/tables/Tables/ArrayColumn_tmpl.cc000066400000000000000000000035261321422335000215620ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: Vector.h 21545 2015-01-22 19:36:35Z gervandiepen $ //# Includes #include //# Instantiate extern templates for often used types. #ifdef AIPS_CXX11 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; } #endif casacore-2.4.1/tables/Tables/BaseColDesc.cc000066400000000000000000000227371321422335000204060ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 scalar column engine handling // that type. if (always || dataManType_p.empty()) { if (dtype_p == TpOther) { dataManType_p = dtypeId_p + "VSCEngine"; } 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-2.4.1/tables/Tables/BaseColDesc.h000066400000000000000000000263411321422335000202430ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/tables/Tables/BaseColumn.cc000066400000000000000000000535711321422335000203270ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN BaseColumn::BaseColumn (const BaseColumnDesc* cdp) : colDescPtr_p(cdp), // The following cast is safe, because this class uses colDesc_p // only to give a const ColumnDesc&. colDesc_p ((BaseColumnDesc*)cdp) {} BaseColumn::~BaseColumn() {} //# By default all functions throw an exception //# to ensure they are called correctly. void BaseColumn::setShape (uInt, const IPosition&) { throw (TableInvOper ("invalid setShape() for column " + colDesc_p.name() + "; only valid for an array")); } void BaseColumn::setShape (uInt, const IPosition&, const IPosition&) { throw (TableInvOper ("invalid setShape() for column " + colDesc_p.name() + "; only valid for an array")); } uInt BaseColumn::ndimColumn() const { throw (TableInvOper ("invalid ndimColumn() for column " + colDesc_p.name() + "; only valid for an array")); return 0; } IPosition BaseColumn::shapeColumn() const { throw (TableInvOper ("invalid shapeColumn() for column " + colDesc_p.name() + "; only valid for an array")); return IPosition(0); } uInt BaseColumn::ndim (uInt) const { throw (TableInvOper ("invalid ndim() for column " + colDesc_p.name() + "; only valid for an array")); return 0; } IPosition BaseColumn::shape (uInt) const { throw (TableInvOper ("invalid shape() for column " + colDesc_p.name() + "; only valid for an array")); return IPosition(0); } IPosition BaseColumn::tileShape (uInt) const { throw (TableInvOper ("invalid tileShape() for column " + colDesc_p.name() + "; only valid for an array")); } Bool BaseColumn::canChangeShape() const { return False; // can not be changed } Bool BaseColumn::canAccessScalarColumn (Bool& reask) const { reask = False; // By default an entire column return False; // can never be accessed } Bool BaseColumn::canAccessArrayColumn (Bool& reask) const { reask = False; // By default an entire column return False; // can never be accessed } Bool BaseColumn::canAccessScalarColumnCells (Bool& reask) const { reask = False; // By default cells in a column return False; // can never be accessed } Bool BaseColumn::canAccessArrayColumnCells (Bool& reask) const { reask = False; // By default cells in a column return False; // can never be accessed } Bool BaseColumn::canAccessSlice (Bool& reask) const { reask = False; // By default a cell slice return False; // can never be accessed } Bool BaseColumn::canAccessColumnSlice (Bool& reask) const { reask = False; // By default a column slice return False; // can never be accessed } void BaseColumn::getSlice (uInt, const Slicer&, void*) const { throw (TableInvOper ("getSlice() not implemented for column " + colDesc_p.name() + "; only valid for an array")); } void BaseColumn::getScalarColumn (void*) const { throw (TableInvOper ("getScalarColumn() not implemented for column " + colDesc_p.name() + "; only valid for a scalar")); } void BaseColumn::getArrayColumn (void*) const { throw (TableInvOper ("getArrayColumn() not implemented for column " + colDesc_p.name() + "; only valid for an array")); } void BaseColumn::getScalarColumnCells (const RefRows&, void*) const { throw (TableInvOper ("getScalarColumnCells() not implemented for column " + colDesc_p.name() + "; only valid for a scalar")); } void BaseColumn::getArrayColumnCells (const RefRows&, void*) const { throw (TableInvOper ("getArrayColumnCells() not implemented for column " + colDesc_p.name() + "; only valid for an array")); } void BaseColumn::getColumnSliceCells (const RefRows&, const Slicer&, void*) const { throw (TableInvOper ("getColumnCells(Slicer&) not implemented for column " + colDesc_p.name() + "; only valid for an array")); } void BaseColumn::getColumnSlice (const Slicer&, void*) const { throw (TableInvOper ("getColumn(Slicer&) not implemented for column " + colDesc_p.name() + "; only valid for an array")); } void BaseColumn::putSlice (uInt, const Slicer&, const void*) { throw (TableInvOper ("putSlice() not implemented for column " + colDesc_p.name() + "; only valid for an array")); } void BaseColumn::putScalarColumn (const void*) { throw (TableInvOper ("putScalarColumn() not implemented for column " + colDesc_p.name() + "; only valid for a scalar")); } void BaseColumn::putArrayColumn (const void*) { throw (TableInvOper ("putArrayColumn() not implemented for column " + colDesc_p.name() + "; only valid for an array")); } void BaseColumn::putScalarColumnCells (const RefRows&, const void*) { throw (TableInvOper ("putScalarColumnCells() not implemented for column " + colDesc_p.name() + "; only valid for a scalar")); } void BaseColumn::putArrayColumnCells (const RefRows&, const void*) { throw (TableInvOper ("putArrayColumnCells() not implemented for column " + colDesc_p.name() + "; only valid for an array")); } void BaseColumn::putColumnSlice (const Slicer&, const void*) { throw (TableInvOper ("putColumn(Slicer&) not implemented for column " + colDesc_p.name() + "; only valid for an array")); } void BaseColumn::putColumnSliceCells (const RefRows&, const Slicer&, const void*) { throw (TableInvOper ("putColumnCells(Slicer&) not implemented for column " + colDesc_p.name() + "; only valid for an array")); } void BaseColumn::makeSortKey (Sort&, CountedPtr&, Int, const void*&) { throw (TableInvOper ("makeSortKey() for column " + colDesc_p.name() + " is only valid for a scalar")); } void BaseColumn::makeRefSortKey (Sort&, CountedPtr&, Int, const Vector&, const void*&) { throw (TableInvOper ("makeSortKey(rownrs) for column " + colDesc_p.name() + " is only valid for a scalar")); } void BaseColumn::freeSortKey (const void*&) { throw (TableInvOper ("freeSortKey() for column " + colDesc_p.name() + " is only valid for a scalar")); } void BaseColumn::allocIterBuf (void*&, void*&, CountedPtr&) { throw (TableInvOper ("allocIterBuf() for column " + colDesc_p.name() + " is only valid for a scalar")); } void BaseColumn::freeIterBuf (void*&, void*&) { throw (TableInvOper ("freeIterBuf() for column " + colDesc_p.name() + " is only valid for a scalar")); } void BaseColumn::getScalar (uInt 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 (uInt 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 (uInt 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 (uInt 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 (uInt rownr, Int& value) const { if (!colDescPtr_p->isScalar()) { throwGetScalar(); } switch (colDescPtr_p->dataType()) { case TpShort: Short vals; get (rownr, &vals); value = vals; return; case TpInt: get (rownr, &value); return; default: throwGetType("Int"); } } void BaseColumn::getScalar (uInt 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 (uInt 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; default: throwGetType("Int64"); } } void BaseColumn::getScalar (uInt 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 TpFloat: get (rownr, &value); return; case TpDouble: double vald; get (rownr, &vald); value = vald; return; default: throwGetType("float"); } } void BaseColumn::getScalar (uInt 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 TpFloat: float valf; get (rownr, &valf); value = valf; return; case TpDouble: get (rownr, &value); return; default: throwGetType("double"); } } void BaseColumn::getScalar (uInt 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 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 (uInt 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 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 (uInt 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 (uInt 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 (uInt 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 (uInt 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 (uInt rownr, const uChar& value) { if (!colDescPtr_p->isScalar()) { throwPutScalar(); } switch (colDescPtr_p->dataType()) { case TpUChar: put (rownr, &value); return; case TpUShort: uShort valus; valus = value; put (rownr, &valus); return; case TpUInt: uInt valui; valui = value; put (rownr, &valui); 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 (uInt rownr, const Short& value) { if (!colDescPtr_p->isScalar()) { throwPutScalar(); } switch (colDescPtr_p->dataType()) { case TpShort: put (rownr, &value); return; case TpInt: Int valui; valui = value; put (rownr, &valui); 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 (uInt rownr, const uShort& value) { if (!colDescPtr_p->isScalar()) { throwPutScalar(); } switch (colDescPtr_p->dataType()) { case TpUShort: put (rownr, &value); return; case TpUInt: uInt valui; valui = value; put (rownr, &valui); 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 (uInt rownr, const Int& value) { if (!colDescPtr_p->isScalar()) { throwPutScalar(); } switch (colDescPtr_p->dataType()) { case TpInt: 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("Int"); } } void BaseColumn::putScalar (uInt rownr, const uInt& value) { if (!colDescPtr_p->isScalar()) { throwPutScalar(); } switch (colDescPtr_p->dataType()) { case TpUInt: 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("uInt"); } } void BaseColumn::putScalar (uInt 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 (uInt 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 (uInt 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 (uInt 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 (uInt 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 (uInt 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 " + colDesc_p.name() + "; only possible for a scalar")); } void BaseColumn::throwPutScalar() const { throw (TableInvOper ("invalid putScalar() for column " + colDesc_p.name() + "; only possible for a scalar")); } void BaseColumn::throwGetType (const String& type) const { throw (TableInvDT ("invalid type promotion in getScalar(" + type + ") for column " + colDesc_p.name() + " with type " + ValType::getTypeStr(colDesc_p.dataType()))); } void BaseColumn::throwPutType (const String& type) const { throw (TableInvDT ("invalid type promotion in putScalar(" + type + ") for column " + colDesc_p.name() + " with type " + ValType::getTypeStr(colDesc_p.dataType()))); } } //# NAMESPACE CASACORE - END casacore-2.4.1/tables/Tables/BaseColumn.h000066400000000000000000000325571321422335000201720ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef TABLES_BASECOLUMN_H #define TABLES_BASECOLUMN_H //# Includes #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class BaseColumnDesc; class ColumnCache; class TableRecord; class RefRows; class IPosition; class Slicer; class Sort; template class Array; template class Vector; // // 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 { return colDesc_p; } // Get nr of rows in the column. virtual uInt nrow() const = 0; // Test if the given cell contains a defined value. virtual Bool isDefined (uInt rownr) const = 0; // Set the shape of the array in the given row. virtual void setShape (uInt rownr, const IPosition& shape); // Set the shape and tile shape of the array in the given row. virtual void setShape (uInt 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 (uInt rownr) const; // Get the shape of an array in a particular cell. virtual IPosition shape (uInt rownr) const; // Get the tile shape of an array in a particular cell. virtual IPosition tileShape (uInt rownr) const; // Ask the data manager if the shape of an existing array can be changed. // Default is no. virtual Bool canChangeShape() const; // Ask if the data manager can handle a scalar column. // Default is never. virtual Bool canAccessScalarColumn (Bool& reask) const; // Ask if the data manager can handle an array column. // Default is never. virtual Bool canAccessArrayColumn (Bool& reask) const; // Ask if the data manager can handle a collection of cells in a // scalar column. Default is never. virtual Bool canAccessScalarColumnCells (Bool& reask) const; // Ask if the data manager can handle a collection of cells in an // array column. Default is never. virtual Bool canAccessArrayColumnCells (Bool& reask) const; // Ask if the data manager can handle a cell slice. // Default is never. virtual Bool canAccessSlice (Bool& reask) const; // Ask if the data manager can handle a column slice. // Default is never. virtual Bool canAccessColumnSlice (Bool& reask) const; // Initialize the rows from startRow till endRow (inclusive) // with the default value defined in the column description. virtual void initialize (uInt startRownr, uInt endRownr) = 0; // Get the value from a particular cell. // This can be a scalar or an array. virtual void get (uInt rownr, void* dataPtr) const = 0; // Get a slice of an N-dimensional array in a particular cell. virtual void getSlice (uInt rownr, const Slicer&, void* dataPtr) const; // Get the vector of all scalar values in a column. virtual void getScalarColumn (void* 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 (void* 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&, void* dataPtr) const; // Get the vector of some scalar values in a column. virtual void getScalarColumnCells (const RefRows& rownrs, void* 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, void* 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&, void* dataPtr) const; // Put the value in a particular cell. // This can be a scalar or an array. virtual void put (uInt rownr, const void* dataPtr) = 0; // Put a slice of an N-dimensional array in a particular cell. virtual void putSlice (uInt rownr, const Slicer&, const void* dataPtr); // Put the vector of all scalar values in the column. virtual void putScalarColumn (const void* 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 void* 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 void* dataPtr); // Get the vector of some scalar values in a column. virtual void putScalarColumnCells (const RefRows& rownrs, const void* 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 void* 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 void* 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 (uInt rownr, Bool& value) const; void getScalar (uInt rownr, uChar& value) const; void getScalar (uInt rownr, Short& value) const; void getScalar (uInt rownr, uShort& value) const; void getScalar (uInt rownr, Int& value) const; void getScalar (uInt rownr, uInt& value) const; void getScalar (uInt rownr, Int64& value) const; void getScalar (uInt rownr, float& value) const; void getScalar (uInt rownr, double& value) const; void getScalar (uInt rownr, Complex& value) const; void getScalar (uInt rownr, DComplex& value) const; void getScalar (uInt rownr, String& value) const; void getScalar (uInt 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 (uInt 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 (uInt rownr, const Bool& value); void putScalar (uInt rownr, const uChar& value); void putScalar (uInt rownr, const Short& value); void putScalar (uInt rownr, const uShort& value); void putScalar (uInt rownr, const Int& value); void putScalar (uInt rownr, const uInt& value); void putScalar (uInt rownr, const float& value); void putScalar (uInt rownr, const double& value); void putScalar (uInt rownr, const Complex& value); void putScalar (uInt rownr, const DComplex& value); void putScalar (uInt rownr, const String& value); void putScalar (uInt rownr, const Char* value) { putScalar (rownr, String(value)); } void putScalar (uInt 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&, CountedPtr& cmpObj, Int order, const void*& dataSave); // Do it only for the given row numbers. virtual void makeRefSortKey (Sort&, CountedPtr& cmpObj, Int order, const Vector& rownrs, const void*& dataSave); // // Free storage on the heap allocated by makeSortkey(). // The pointer will be set to zero. virtual void freeSortKey (const void*& 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, CountedPtr& 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; //# This ColumnDesc object is created to be able to return //# a const ColumnDesc& by function columnDesc(). ColumnDesc colDesc_p; }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/tables/Tables/BaseTabIter.cc000066400000000000000000000130551321422335000204150ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (BaseTable* btp, const Block& keys, const Block >& cmp, const Block& order, int option) : lastRow_p (0), nrkeys_p (keys.nelements()), keyChangeAtLastNext_p(""), colPtr_p (keys.nelements()), cmpObj_p (cmp), lastVal_p (keys.nelements()), curVal_p (keys.nelements()) { // 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::InsSort) { sortopt = Sort::InsSort; } Block ord(nrkeys_p, Sort::Ascending); for (uInt i=0; isort (keys, cmpObj_p, ord, sortopt)); } sortTab_p->link(); // 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]); } } 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) { // 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]); } // Link against the table (ie. increase its ref.count). sortTab_p = that.sortTab_p; sortTab_p->link(); } BaseTableIterator::~BaseTableIterator() { // Delete the value buffers. for (uInt i=0; ifreeIterBuf (lastVal_p[i], curVal_p[i]); } // Unlink from the table and delete it. BaseTable::unlink (sortTab_p); } void BaseTableIterator::reset() { lastRow_p = 0; } BaseTable* BaseTableIterator::next() { uInt i; // Allocate a RefTable to represent the rows in the iteration group. RefTable* itp = sortTab_p->makeRefTable (False, 0); if (lastRow_p >= sortTab_p->nrow()) { return itp; // the end of the table } // Add the last found rownr to this iteration group. itp->addRownr (lastRow_p); for (i=0; iget (lastRow_p, lastVal_p[i]); } Bool match; uInt nr = sortTab_p->nrow(); while (++lastRow_p < nr) { match = True; for (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 itp; } void BaseTableIterator::copyState(const BaseTableIterator &other) { lastRow_p = other.lastRow_p; keyChangeAtLastNext_p = other.keyChangeAtLastNext_p; } } //# NAMESPACE CASACORE - END casacore-2.4.1/tables/Tables/BaseTabIter.h000066400000000000000000000113241321422335000202540ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 comare object is null, the default ObjCompare will be used. BaseTableIterator (BaseTable*, const Block& columnNames, const Block >&, const Block& orders, int option); // Clone this iterator. BaseTableIterator* clone() const; virtual ~BaseTableIterator(); // Reset the iterator (i.e. restart iteration). virtual void reset(); // Return the next group. virtual BaseTable* next(); virtual void copyState(const BaseTableIterator &); // Report Name of slowest sort column that changed 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: BaseTable* sortTab_p; //# Table sorted in iteration order uInt 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&); private: // Assignment is not needed, because the assignment operator in // the envelope class TableIterator has reference semantics. // Declaring it private, makes it unusable. BaseTableIterator& operator= (const BaseTableIterator&); Block lastVal_p; //# last value per column Block curVal_p; //# current value per column }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/tables/Tables/BaseTable.cc000066400000000000000000001135271321422335000201170ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include #include #include #include #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, uInt nrrow) : nrlink_p (0), nrrow_p (nrrow), nrrowToAdd_p(0), tdescPtr_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 tdescPtr_p; //# 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); } } } void BaseTable::link() { nrlink_p++; #ifdef AIPS_TRACE cout << "BaseTable::link: " << nrlink_p << ' ' << name_p << ' ' << this << endl; #endif } void BaseTable::unlink (BaseTable* btp) { #ifdef AIPS_TRACE cout << "BaseTable::unlink: " << btp->nrlink_p << ' ' << btp->name_p << ' ' << btp; if (btp->nrlink_p == 1) { cout << " gets destructed"; } cout << endl; #endif btp->nrlink_p--; if (btp->nrlink_p == 0) { delete btp; } } 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, ""); } } } } Bool BaseTable::makeTableDir() { //# Exit if the table directory has already been created. if (madeDir_p) { return False; } //# Check option. if (!openedForWrite()) { throw (TableInvOpt ("BaseTable::makeTableDir", "must be Table::New, NewNoReplace or Update")); } //# 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 { 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. ios.open (Table::fileName(name_p), 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. ios.putstart ("Table", 2); ios << 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 (); } void BaseTable::setTableChanged() {} void BaseTable::markForDelete (Bool callback, const String& oldName) { AlwaysAssert (!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 (!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. //# (cast is necessary to bypass non-constness). 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 (uInt, Bool) { throw (TableInvOper ("Table: cannot add a row to table " + name_p)); } void BaseTable::removeRow (uInt) { 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 (Int 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, (uInt)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")); return 0; } //# Sort a table. BaseTable* BaseTable::sort (const Block& names, const Block >& cmpObj, const Block& order, int option) { 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); } //# Do the actual sort. BaseTable* BaseTable::doSort (PtrBlock& sortCol, const Block >& cmpObj, const Block& order, int option) { uInt i; uInt nrkey = sortCol.nelements(); //# Create a sort object. //# Pass all keys (and their data) to it. Sort sortobj; PtrBlock dataSave(nrkey); // to remember data blocks Block > cmp(cmpObj); for (i=0; imakeSortKey (sortobj, cmp[i], order[i], dataSave[i]); } //# Create a reference table. //# This table will NOT be in row order. uInt nrrow = nrow(); RefTable* resultTable = makeRefTable (False, nrrow); //# 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); adjustRownrs (nrrow, rows, False); resultTable->setNrrow (nrrow); for (i=0; ifreeSortKey (dataSave[i]); } return resultTable; } RefTable* BaseTable::makeRefTable (Bool rowOrder, uInt initialNrrow) { RefTable* rtp = new RefTable(this, rowOrder, initialNrrow); return rtp; } //# No rownrs have to be adjusted and they are by default in ascending order. Bool BaseTable::adjustRownrs (uInt, Vector&, Bool) const { return True; } BaseTable* BaseTable::select (uInt maxRow, uInt offset) { if (offset > nrow()) { offset = nrow(); } if (maxRow == 0 || maxRow > nrow() ) { maxRow = nrow() - offset; } if (offset == 0 && maxRow == nrow()) { return this; } Vector rownrs(maxRow); indgen(rownrs, offset); return select(rownrs); } // Do the row selection. BaseTable* BaseTable::select (const TableExprNode& node, uInt maxRow, uInt 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). if (!node.table().isNull() && node.table().nrow() != this->nrow()) { throw (TableInvExpr ("select expression for table " + node.table().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. SPtrHolder resultTable (makeRefTable (True, 0)); Bool val; uInt nrrow = nrow(); TableExprId id; for (uInt 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.transfer(); } BaseTable* BaseTable::select (const Vector& rownrs) { AlwaysAssert (!isNull(), AipsError); RefTable* rtp = new RefTable(this, rownrs); return rtp; } BaseTable* BaseTable::select (const Block& mask) { AlwaysAssert (!isNull(), AipsError); RefTable* rtp = new RefTable(this, Vector(mask)); return rtp; } BaseTable* BaseTable::project (const Block& names) { AlwaysAssert (!isNull(), AipsError); RefTable* rtp = new RefTable(this, Vector(names)); return rtp; } //# And (intersect) 2 tables and return a new table. BaseTable* 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; // this is root } if (that->nrow() == that->root()->nrow()) { return this; // that is root } //# There is no root table involved, so we have to deal with RefTables. //# Get both rownr arrays and sort them if not in row order. //# Sorting means that the array is allocated on the heap, which has //# to be deleted afterwards. Bool allsw1, allsw2; uInt* inx1; uInt* inx2; uInt nr1 = this->logicRows (inx1, allsw1); uInt nr2 = that->logicRows (inx2, allsw2); RefTable* rtp = makeRefTable (True, 0); // will be in row order rtp->refAnd (nr1, inx1, nr2, inx2); // store rownrs in new RefTable if (allsw1) { delete [] inx1; } if (allsw2) { delete [] inx2; } return rtp; } //# Or (union) 2 tables and return a new table. BaseTable* BaseTable::tabOr (BaseTable* that) { AlwaysAssert (!isNull(), AipsError); //# Check if both table 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(); } //# There is no root table involved, so we have to deal with RefTables. //# Get both rownr arrays and sort them if not in row order. //# Sorting means that the array is allocated on the heap, which has //# to be deleted afterwards. Bool allsw1, allsw2; uInt* inx1; uInt* inx2; uInt nr1 = this->logicRows (inx1, allsw1); uInt nr2 = that->logicRows (inx2, allsw2); RefTable* rtp = makeRefTable (True, 0); // will be in row order rtp->refOr (nr1, inx1, nr2, inx2); // store rownrs in new RefTable if (allsw1) { delete [] inx1; } if (allsw2) { delete [] inx2; } return rtp; } //# Subtract (difference) 2 tables and return a new table. BaseTable* 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 and sort them if not in row order. //# Sorting means that the array is allocated on the heap, which has //# to be deleted afterwards. Bool allsw1, allsw2; uInt* inx1; uInt* inx2; uInt nr1 = this->logicRows (inx1, allsw1); uInt nr2 = that->logicRows (inx2, allsw2); RefTable* rtp = makeRefTable (True, 0); // will be in row order rtp->refSub (nr1, inx1, nr2, inx2); // store rownrs in new RefTable if (allsw1) { delete [] inx1; } if (allsw2) { delete [] inx2; } return rtp; } //# Xor 2 tables and return a new table. BaseTable* 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 and sort them if not in row order. //# Sorting means that the array is allocated on the heap, which has //# to be deleted afterwards. Bool allsw1, allsw2; uInt* inx1; uInt* inx2; uInt nr1 = this->logicRows (inx1, allsw1); uInt nr2 = that->logicRows (inx2, allsw2); RefTable* rtp = makeRefTable (True, 0); // will be in row order rtp->refXor (nr1, inx1, nr2, inx2); // store rownrs in new RefTable if (allsw1) { delete [] inx1; } if (allsw2) { delete [] inx2; } return rtp; } //# Negate a table (i.e. take all rows from the root not in table). BaseTable* 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 both rownr arrays and sort them if not in row order. //# Sorting means that the array is allocated on the heap, which has //# to be deleted. Bool allsw1; uInt* inx1; uInt nr1 = this->logicRows (inx1, allsw1); RefTable* rtp = makeRefTable (True, 0); // will be in row order rtp->refNot (nr1, inx1, root()->nrow()); // store rownrs in new RefTable if (allsw1) { delete [] inx1; } 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. uInt BaseTable::logicRows (uInt*& inx, Bool& allsw) { AlwaysAssert (!isNull(), AipsError); allsw = False; inx = RefTable::getStorage (*rowStorage()); uInt nr = nrow(); if (!rowOrder()) { //# rows are not in order, so sort them. //# They have to be copied, because the original should not be changed. uInt* inxcp = new uInt[nr]; objcopy (inxcp, inx, nr); GenSort::sort (inxcp, nr); inx = inxcp; allsw = True; } return nr; } BaseTableIterator* BaseTable::makeIterator (const Block& names, const Block >& cmpObj, const Block& order, int option) { AlwaysAssert (!isNull(), AipsError); if (names.nelements() != order.nelements() || names.nelements() != cmpObj.nelements()) { throw (TableInvOper ("TableIterator: Unequal block lengths")); } BaseTableIterator* bti = new BaseTableIterator (this, names, cmpObj, order, option); return bti; } const TableDesc& BaseTable::makeTableDesc() const { if (tdescPtr_p == 0) { const_cast(this)->tdescPtr_p = new TableDesc(); } 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 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 Vector; 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: // Initialize the object. BaseTable (const String& tableName, int tableOption, uInt nrrow); virtual ~BaseTable(); // Link to this BaseTable object (i.e. increase reference count). void link(); // Unlink from a BaseTable. // Delete it if no more references. static void unlink (BaseTable*); // 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 == 0 ? makeTableDesc() : *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. uInt 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 (uInt 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 (uInt rownr); 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. BaseTable* select (const TableExprNode&, uInt maxRow, uInt offset); // Select maxRow rows and skip first offset rows. maxRow=0 means all. BaseTable* select (uInt maxRow, uInt offset); // Select rows using a vector of row numbers. BaseTable* 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. BaseTable* select (const Block& mask); // Project the given columns (i.e. select the columns). BaseTable* project (const Block& columnNames); //# Virtually concatenate all tables in this column. //# The column cells must contain tables with the same description. //#// BaseTable* concatenate (const String& columnName); // Do logical operations on a table. // // intersection with another table BaseTable* tabAnd (BaseTable*); // union with another table BaseTable* tabOr (BaseTable*); // subtract another table BaseTable* tabSub (BaseTable*); // xor with another table BaseTable* tabXor (BaseTable*); // take complement BaseTable* tabNot (); // // Sort a table on one or more columns of scalars. BaseTable* sort (const Block& columnNames, const Block >& compareObjects, const Block& sortOrder, int sortOption); // Create an iterator. BaseTableIterator* makeIterator (const Block& columnNames, const Block >&, const Block& orders, int option); // 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 (uInt 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 BaseTable* doSort (PtrBlock&, const Block >&, const Block& sortOrder, int sortOption); // Create a RefTable object. RefTable* makeRefTable (Bool rowOrder, uInt initialNrrow); // Check if the row number is valid. // It throws an exception if out of range. void checkRowNumber (uInt rownr) const { if (rownr >= nrrow_p + nrrowToAdd_p) checkRowNumberThrow (rownr); } // Get the table's trace-id. int traceId() const { return itsTraceId; } protected: uInt nrlink_p; //# #references to this table uInt nrrow_p; //# #rows in this table uInt nrrowToAdd_p; //# #rows to be added TableDesc* 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. 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: // Copy constructor is forbidden, because copying a table requires // some more knowledge (like table name of result). // Declaring it private, makes it unusable. BaseTable (const BaseTable&); // Assignment is forbidden, because copying a table requires // some more knowledge (like table name of result). // Declaring it private, makes it unusable. BaseTable& operator= (const BaseTable&); // 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 (uInt 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. uInt logicRows (uInt*& rownrs, Bool& allocated); // 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& makeTableDesc() const; // Make the name absolute. // It first checks if the name contains valid characters (not only . and /). String makeAbsoluteName (const String& name) const; }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/tables/Tables/BitFlagsEngine.h000066400000000000000000000003601321422335000207460ustar00rootroot00000000000000#ifndef TABLES_TABLES_STUB_BITFLAGSENGINE_H #define TABLES_TABLES_STUB_BITFLAGSENGINE_H #include #warning "Tables/BitFlagsEngine.h has been deprecated; use DataMan/BitFlagsEngine.h instead" #endif casacore-2.4.1/tables/Tables/ColDescSet.cc000066400000000000000000000177171321422335000202710ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN ColumnDescSet::ColumnDescSet() : cols_p (ColumnDesc()), colSeq_p (0) { //# Register the main DataManagers if not done yet. DataManager::registerMainCtor(); } ColumnDescSet::ColumnDescSet (const ColumnDescSet& that) : cols_p (ColumnDesc()), colSeq_p (0) { operator= (that); } ColumnDescSet::~ColumnDescSet() {} ColumnDescSet& ColumnDescSet::operator= (const ColumnDescSet& that) { if (this != &that) { cols_p = that.cols_p; uInt nrcol = ncolumn(); colSeq_p.resize (nrcol); //# Now we have to fill in the column order, which is the //# same as the order in the source. //# We have to point to our own ColumnDesc objects. for (uInt i=0; i 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; 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 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.ndefined(); } // Test if a column is defined in this set. Bool isDefined (const String& name) const { return (cols_p.isDefined (name)); } // 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. SimpleOrderedMap 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-2.4.1/tables/Tables/ColumnCache.cc000066400000000000000000000031011321422335000204400ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# Includes #include namespace casacore { //# NAMESPACE CASACORE - BEGIN ColumnCache::ColumnCache() : itsIncr (1) { invalidate(); } void ColumnCache::set (uInt startRow, uInt endRow, const void* dataPtr) { itsStart = startRow; itsEnd = endRow; itsData = dataPtr; } } //# NAMESPACE CASACORE - END casacore-2.4.1/tables/Tables/ColumnCache.h000066400000000000000000000113701321422335000203110ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef TABLES_COLUMNCACHE_H #define TABLES_COLUMNCACHE_H //# Includes #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 (uInt increment); // Set the start and end row number for which the given data pointer // is valid. void set (uInt startRow, uInt 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. Int offset (uInt 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. uInt start() const {return itsStart;} uInt end() const {return itsEnd;} uInt incr() const {return itsIncr;} private: uInt itsStart; uInt itsEnd; uInt itsIncr; const void* itsData; }; inline void ColumnCache::setIncrement (uInt increment) { itsIncr = increment; } inline void ColumnCache::invalidate() { set (1, 0, 0); } inline Int ColumnCache::offset (uInt rownr) const { return rownritsEnd ? -1 : Int((rownr-itsStart)*itsIncr); } inline const void* ColumnCache::dataPtr() const { return itsData; } /* inline uInt ColumnCache::start() const { return itsStart; } inline uInt ColumnCache::end() const { return itsEnd; } inline uInt ColumnCache::incr() const { return itsIncr; } */ } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/tables/Tables/ColumnDesc.cc000066400000000000000000000215601321422335000203240ustar00rootroot00000000000000//# ColumnDesc.cc: Envelope class for description of a table column //# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN 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 ? False : True); } 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 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) { //# First register all subclasses if not done yet. theirMutexedInit.exec(); uInt version; ios >> version; String tp; ios >> tp; if (allocated_p) { delete colPtr_p; } allocated_p = True; colPtr_p = theirRegisterMap(tp)(tp); 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; } } //# Initialize the static variables for the class registration. MutexedInit ColumnDesc::theirMutexedInit (doRegisterMainCtor); SimpleOrderedMap ColumnDesc::theirRegisterMap (ColumnDesc::unknownColumnDesc); //# The default "ctor" function for unknown types. BaseColumnDesc* ColumnDesc::unknownColumnDesc (const String& name) { throw (TableUnknownDesc(name)); return 0; } //# Register a mapping. void ColumnDesc::registerCtor (const String& name, ColumnDescCtor* func) { ScopedMutexLock lock(theirMutexedInit.mutex()); unlockedRegisterCtor (name, func); } //# Get a ColumnDesc constructor. //# Return default function if undefined. ColumnDesc::ColumnDescCtor* ColumnDesc::getCtor (const String& name) { ScopedMutexLock lock(theirMutexedInit.mutex()); return *(theirRegisterMap.isDefined (name)); } //# Register the main "static constructors" of all XColumnDesc classes. void ColumnDesc::doRegisterMainCtor (void*) { ScalarColumnDesc scdb("x"); unlockedRegisterCtor (scdb.className(), scdb.makeDesc); ScalarColumnDesc scduc("x"); unlockedRegisterCtor (scduc.className(), scduc.makeDesc); ScalarColumnDesc scds("x"); unlockedRegisterCtor (scds.className(), scds.makeDesc); ScalarColumnDesc scdus("x"); unlockedRegisterCtor (scdus.className(), scdus.makeDesc); ScalarColumnDesc scdi("x"); unlockedRegisterCtor (scdi.className(), scdi.makeDesc); ScalarColumnDesc scdui("x"); unlockedRegisterCtor (scdui.className(), scdui.makeDesc); ScalarColumnDesc scdf("x"); unlockedRegisterCtor (scdf.className(), scdf.makeDesc); ScalarColumnDesc scdd("x"); unlockedRegisterCtor (scdd.className(), scdd.makeDesc); ScalarColumnDesc scdcx("x"); unlockedRegisterCtor (scdcx.className(), scdcx.makeDesc); ScalarColumnDesc scddx("x"); unlockedRegisterCtor (scddx.className(), scddx.makeDesc); ScalarColumnDesc scdst("x"); unlockedRegisterCtor (scdst.className(), scdst.makeDesc); ScalarRecordColumnDesc srcd ("x"); unlockedRegisterCtor (srcd.className(), srcd.makeDesc); ArrayColumnDesc acdb("x"); unlockedRegisterCtor (acdb.className(), acdb.makeDesc); ArrayColumnDesc acduc("x"); unlockedRegisterCtor (acduc.className(), acduc.makeDesc); ArrayColumnDesc acds("x"); unlockedRegisterCtor (acds.className(), acds.makeDesc); ArrayColumnDesc acdus("x"); unlockedRegisterCtor (acdus.className(), acdus.makeDesc); ArrayColumnDesc acdi("x"); unlockedRegisterCtor (acdi.className(), acdi.makeDesc); ArrayColumnDesc acdui("x"); unlockedRegisterCtor (acdui.className(), acdui.makeDesc); ArrayColumnDesc acdf("x"); unlockedRegisterCtor (acdf.className(), acdf.makeDesc); ArrayColumnDesc acdd("x"); unlockedRegisterCtor (acdd.className(), acdd.makeDesc); ArrayColumnDesc acdcx("x"); unlockedRegisterCtor (acdcx.className(), acdcx.makeDesc); ArrayColumnDesc acddx("x"); unlockedRegisterCtor (acddx.className(), acddx.makeDesc); ArrayColumnDesc acdst("x"); unlockedRegisterCtor (acdst.className(), acdst.makeDesc); SubTableDesc std("x", "", TableDesc()); unlockedRegisterCtor (std.className(), std.makeDesc); } } //# NAMESPACE CASACORE - END casacore-2.4.1/tables/Tables/ColumnDesc.h000066400000000000000000000374151321422335000201740ustar00rootroot00000000000000//# ColumnDesc.h: an envelope class for column descriptions in tables //# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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*); // Register the main data managers (if not done yet). // It is fully thread-safe. static void registerMainCtor() { theirMutexedInit.exec(); } private: // Register a constructor without doing a mutex lock. static void unlockedRegisterCtor (const String& type, ColumnDescCtor* func) { theirRegisterMap.define (type, func); } // 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 MutexedInit theirMutexedInit; static SimpleOrderedMap theirRegisterMap; // Serve as default function for theirRegisterMap (see below), // which catches all unknown XXColumnDesc class names. // //
      • TableUnknownDesc // static BaseColumnDesc* unknownColumnDesc (const String& name); // Do the actual (thread-safe) registration of the main data managers. static void doRegisterMainCtor (void*); private: // 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-2.4.1/tables/Tables/ColumnSet.cc000066400000000000000000000761261321422335000202110ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN #define BLOCKDATAMANVAL(I) ((DataManager*)(blockDataMan_p[I])) #define COLMAPVAL(I) ((PlainColumn*)(colMap_p.getVal(I))) #define COLMAPNAME(NAME) ((PlainColumn*)(colMap_p(NAME))) ColumnSet::ColumnSet (TableDesc* tdesc, const StorageOption& opt) : tdescPtr_p (tdesc), storageOpt_p (opt), multiFile_p (0), baseTablePtr_p (0), lockPtr_p (0), colMap_p (static_cast(0), tdesc->ncolumn()), 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.define (cd.name(), cd.makeColumn(this)); } } ColumnSet::~ColumnSet() { uInt i; for (i=0; icolumnDesc(columnName); // check if column exists return COLMAPNAME(columnName); } //# We cannot simply return COLMAPVAL(columnIndex), because the order of //# the columns in the description is important. So 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 (uInt nrrow, Bool bigEndian, const TSMOption& tsmOption, Table& tab) { uInt i; for (i=0; isetEndian (bigEndian); BLOCKDATAMANVAL(i)->setTsmOption (tsmOption); } for (i=0; icreateDataManagerColumn(); } //# Delete data managers without columns. uInt nr = 0; for (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); uInt i; //# Link the data managers to the table. for (i=from; ilinkToTable (tab); } //# Now give the data managers the opportunity to create files as needed. //# Thereafter to prepare things. for (i=from; icreate (nrrow_p); } prepareSomeDataManagers (from); } void ColumnSet::prepareSomeDataManagers (uInt from) { uInt i, j; for (i=from; icanReallocateColumns()) { for (j=0; jdataManagerColumn(); column = BLOCKDATAMANVAL(i)->reallocateColumn (column); } } } for (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 = new MultiFile (tab.tableName() + "/table.mf", opt, storageOpt_p.blockSize()); } else { multiFile_p = new MultiHDF5 (tab.tableName() + "/table.mfh5", opt, storageOpt_p.blockSize()); } } // Pass it to the data managers. for (uInt i=from; isetMultiFile (multiFile_p); } } } uInt ColumnSet::resync (uInt 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; iresync1 (nrrow); if (nrr > nrrow) { nrrow = nrr; } dataManChanged_p[i] = False; } } nrrow_p = nrrow; } return nrrow_p; } void ColumnSet::invalidateColumnCaches() { for (uInt i=0; icolumnCache().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 (uInt nrrow) { // First add row to storage managers, thereafter to virtual engines. for (uInt i=0; iisStorageManager()) { BLOCKDATAMANVAL(i)->addRow (nrrow); } } for (uInt i=0; iisStorageManager()) { BLOCKDATAMANVAL(i)->addRow (nrrow); } } nrrow_p += nrrow; } //# Remove a row from all data managers. void ColumnSet::removeRow (uInt 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; iremoveRow (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.define (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 AipsError& x) { error = True; msg = x.getMesg(); //# 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.remove (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.define (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 AipsError& x) { error = True; msg = x.getMesg(); 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. SimpleOrderedMap dmCounts = checkRemoveColumn (columnNames); // Write lock table. checkWriteLock (True); // Remove all data managers possible. for (uInt i=0; i(const_cast(dmCounts.getKey(i))); 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(dmPtr) >= 0) { DataManagerColumn* dmcolPtr = colPtr->dataManagerColumn(); dmPtr->removeColumn (dmcolPtr); } delete colPtr; colMap_p.remove (name); } autoReleaseLock(); } SimpleOrderedMap 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. SimpleOrderedMap dmCounts(0, 16); for (uInt i=0; idataManager())++; } // If all columns in a data manager are to be deleted, set count to -1. for (uInt i=0; i(const_cast(dmCounts.getKey(i))); if (dmCounts.getVal(i) == Int(dmPtr->ncolumn())) { dmCounts.getVal(i) = -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(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); colMap_p.rename (newName, oldName); 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.ndefined(); Vector columns(ncol); uInt nc=0; for (uInt j=0; jdataManager() == dmPtr) { columns(nc++) = colMap_p.getKey(j); } } if (nc > 0) { columns.resize (nc, True); subrec.define ("COLUMNS", columns); rec.defineRecord (nrec, subrec); nrec++; } } } return rec; } //# Initialize rows. void ColumnSet::initialize (uInt startRow, uInt 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); uInt i; for (i=nrold; i Int64(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 (i=0; incolumn() > 0) { nr++; } } ios << nr; for (i=0; incolumn() > 0) { ios << BLOCKDATAMANVAL(i)->dataManagerType(); ios << BLOCKDATAMANVAL(i)->sequenceNr(); } } //# Now write all columns. for (i=0; iputFile (ios, attr); } } //# Now write out the data in all data managers. //# Keep track if a data manager indeed wrote something. MemoryIO memio; AipsIO aio(&memio); for (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; } uInt ColumnSet::getFile (AipsIO& ios, Table& tab, uInt 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). for (i=0; igetFile (ios, *this, TableAttr(tab)); }else{ getColumn(i)->getFile (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; iopen1 (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.ndefined(); if (other.colMap_p.ndefined() != 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-2.4.1/tables/Tables/ColumnSet.h000066400000000000000000000321061321422335000200410ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef TABLES_COLUMNSET_H #define TABLES_COLUMNSET_H //# Includes #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; template class Vector; // // 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; } // 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 (uInt 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 (uInt nrrow); // Remove a row from all data managers. // It will throw an exception if not possible. void removeRow (uInt 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. uInt 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 (uInt startRownr, uInt 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. uInt getFile (AipsIO&, Table& tab, uInt 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. uInt resync (uInt 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. SimpleOrderedMap 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; MultiFileBase* multiFile_p; Int64 nrrow_p; //# #rows BaseTable* baseTablePtr_p; TableLockData* lockPtr_p; //# lock object SimpleOrderedMap 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 uInt 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-2.4.1/tables/Tables/ColumnsIndex.cc000066400000000000000000000530071321422335000207010ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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 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 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); uInt 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 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; } uInt ColumnsIndex::bsearch (Bool& found, const Block& fieldPtrs) const { found = False; Int lower = 0; Int upper = itsUniqueIndex.nelements() - 1; Int 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, Int 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 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; } uInt ColumnsIndex::getRowNumber (Bool& found, const Record& key) { copyKey (itsLowerFields, key); return getRowNumber (found); } uInt ColumnsIndex::getRowNumber (Bool& found) { if (!isUnique()) { throw (TableError ("ColumnsIndex::getRowNumber only possible " "when the index keys are unique")); } // Read the data (if needed). readData(); uInt inx = bsearch (found, itsLowerFields); if (found) { inx = itsDataInx[inx]; } return inx; } Vector ColumnsIndex::getRowNumbers (const Record& key) { copyKey (itsLowerFields, key); return getRowNumbers(); } Vector ColumnsIndex::getRowNumbers() { // Read the data (if needed). readData(); Bool found; uInt inx = bsearch (found, itsLowerFields); Vector rows; if (found) { fillRowNumbers (rows, inx, inx+1); } return rows; } Vector ColumnsIndex::getRowNumbers (const Record& lowerKey, const Record& upperKey, Bool lowerInclusive, Bool upperInclusive) { copyKey (itsLowerFields, lowerKey); copyKey (itsUpperFields, upperKey); return getRowNumbers (lowerInclusive, upperInclusive); } Vector 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. uInt 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). uInt end = bsearch (found, itsUpperFields); if (found && upperInclusive) { end++; } Vector rows; if (start < end) { fillRowNumbers (rows, start, end); } return rows; } void ColumnsIndex::fillRowNumbers (Vector& rows, uInt start, uInt end) const { start = itsUniqueInx[start]; if (end < itsUniqueIndex.nelements()) { end = itsUniqueInx[end]; } else { end = itsDataIndex.nelements(); } uInt nr = end-start; rows.resize (nr); Bool deleteIt; uInt* 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 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; // 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. // } // } // // // 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). // Vector 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, // Int 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. // uInt 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, Int 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. // uInt getRowNumber (Bool& found); uInt 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. // Vector getRowNumbers(); 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. // 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. // Vector getRowNumbers (Bool lowerInclusive, Bool upperInclusive); Vector 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. uInt 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, Int index); // Fill the row numbers vector for the given start till end in the // itsUniqueIndex vector (end is not inclusive). void fillRowNumbers (Vector& rows, uInt start, uInt 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; uInt 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; uInt* itsDataInx; //# pointer to data in itsDataIndex uInt* 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-2.4.1/tables/Tables/ColumnsIndexArray.cc000066400000000000000000000433641321422335000217050ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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 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 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); uInt 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 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; } uInt ColumnsIndexArray::bsearch (Bool& found, void* fieldPtr) const { found = False; Int lower = 0; Int upper = itsUniqueIndex.nelements() - 1; Int 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, Int 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 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; } uInt ColumnsIndexArray::getRowNumber (Bool& found, const Record& key) { ColumnsIndex::copyKeyField (itsLowerField, itsDataType, key); return getRowNumber (found); } uInt ColumnsIndexArray::getRowNumber (Bool& found) { if (!isUnique()) { throw (TableError ("ColumnsIndexArray::getRowNumber only possible " "when the index keys are unique")); } // Read the data (if needed). readData(); uInt inx = bsearch (found, itsLowerField); if (found) { inx = itsRownrs[itsDataInx[inx]]; } return inx; } Vector ColumnsIndexArray::getRowNumbers (const Record& key, Bool unique) { ColumnsIndex::copyKeyField (itsLowerField, itsDataType, key); return getRowNumbers (unique); } Vector ColumnsIndexArray::getRowNumbers (Bool unique) { // Read the data (if needed). readData(); Bool found; uInt inx = bsearch (found, itsLowerField); Vector rows; if (found) { fillRowNumbers (rows, inx, inx+1, unique); } return rows; } Vector 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); } Vector 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. uInt 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). uInt end = bsearch (found, itsUpperField); if (found && upperInclusive) { end++; } Vector rows; if (start < end) { fillRowNumbers (rows, start, end, unique); } return rows; } void ColumnsIndexArray::fillRowNumbers (Vector& rows, uInt start, uInt end, Bool unique) const { start = itsUniqueInx[start]; if (end < itsUniqueIndex.nelements()) { end = itsUniqueInx[end]; } else { end = itsDataIndex.nelements(); } uInt nr = end-start; rows.resize (nr); Bool deleteIt; uInt* rowStorage = rows.getStorage (deleteIt); for (uInt 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); uInt nrrow = arrCol.nrow(); if (nrrow > 0) { Block nrel(nrrow, uInt(0)); Array arr = arrCol(0); uInt npts = arr.nelements(); nrel[0] = npts; result.resize (nrrow*npts); Bool deleteIt; uChar* data = result.getStorage(deleteIt); objmove (data, arr.getStorage(deleteIt), npts); data += npts; for (uInt i=1; i arr = arrCol(i); uInt n = arr.nelements(); nrel[i] = n; if (npts+n > result.nelements()) { result.resize (npts+n, True); } data = result.getStorage(deleteIt) + npts; objmove (data, arr.getStorage(deleteIt), n); npts += n; } } result.resize (npts, True); fillRownrs (npts, nrel); } } void ColumnsIndexArray::getArray (Vector& result, const String& name) { ArrayColumn arrCol (itsTable, name); uInt nrrow = arrCol.nrow(); if (nrrow > 0) { Block nrel(nrrow, uInt(0)); Array arr = arrCol(0); uInt npts = arr.nelements(); nrel[0] = npts; result.resize (nrrow*npts); Bool deleteIt; Short* data = result.getStorage(deleteIt); objmove (data, arr.getStorage(deleteIt), npts); data += npts; for (uInt i=1; i arr = arrCol(i); uInt n = arr.nelements(); nrel[i] = n; if (npts+n > result.nelements()) { result.resize (npts+n, True); } data = result.getStorage(deleteIt) + npts; objmove (data, arr.getStorage(deleteIt), n); npts += n; } } result.resize (npts, True); fillRownrs (npts, nrel); } } void ColumnsIndexArray::getArray (Vector& result, const String& name) { ArrayColumn arrCol (itsTable, name); uInt nrrow = arrCol.nrow(); if (nrrow > 0) { Block nrel(nrrow, uInt(0)); Array arr = arrCol(0); uInt npts = arr.nelements(); nrel[0] = npts; result.resize (nrrow*npts); Bool deleteIt; Int* data = result.getStorage(deleteIt); objmove (data, arr.getStorage(deleteIt), npts); data += npts; for (uInt i=1; i arr = arrCol(i); uInt n = arr.nelements(); nrel[i] = n; if (npts+n > result.nelements()) { result.resize (npts+n, True); } data = result.getStorage(deleteIt) + npts; objmove (data, arr.getStorage(deleteIt), n); npts += n; } } result.resize (npts, True); fillRownrs (npts, nrel); } } void ColumnsIndexArray::getArray (Vector& result, const String& name) { ArrayColumn arrCol (itsTable, name); uInt nrrow = arrCol.nrow(); if (nrrow > 0) { Block nrel(nrrow, uInt(0)); Array arr = arrCol(0); uInt npts = arr.nelements(); nrel[0] = npts; result.resize (nrrow*npts); Bool deleteIt; uInt* data = result.getStorage(deleteIt); objmove (data, arr.getStorage(deleteIt), npts); data += npts; for (uInt i=1; i arr = arrCol(i); uInt n = arr.nelements(); nrel[i] = n; if (npts+n > result.nelements()) { result.resize (npts+n, True); } data = result.getStorage(deleteIt) + npts; objmove (data, arr.getStorage(deleteIt), n); npts += n; } } result.resize (npts, True); fillRownrs (npts, nrel); } } void ColumnsIndexArray::getArray (Vector& result, const String& name) { ArrayColumn arrCol (itsTable, name); uInt nrrow = arrCol.nrow(); if (nrrow > 0) { Block nrel(nrrow, uInt(0)); Array arr = arrCol(0); uInt npts = arr.nelements(); nrel[0] = npts; result.resize (nrrow*npts); Bool deleteIt; String* data = result.getStorage(deleteIt); objmove (data, arr.getStorage(deleteIt), npts); data += npts; for (uInt i=1; i arr = arrCol(i); uInt n = arr.nelements(); nrel[i] = n; if (npts+n > result.nelements()) { result.resize (npts+n, True); } data = result.getStorage(deleteIt) + npts; objmove (data, arr.getStorage(deleteIt), n); npts += n; } } result.resize (npts, True); fillRownrs (npts, nrel); } } void ColumnsIndexArray::fillRownrs (uInt npts, const Block& nrel) { itsRownrs.resize (npts); uInt* data = itsRownrs.storage(); for (uInt 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, 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"; // uInt 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"; // Vector 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. // uInt getRowNumber (Bool& found); uInt 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. // Vector getRowNumbers (Bool unique=False); Vector 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. // Vector getRowNumbers (Bool lowerInclusive, Bool upperInclusive, Bool unique=False); Vector 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. uInt 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, Int 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, uInt start, uInt 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); // // Fill the rownrs belonging to each array value. void fillRownrs (uInt npts, const Block& nrel); private: Table itsTable; uInt 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 uInt* itsDataInx; //# pointer to data in itsDataIndex uInt* 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-2.4.1/tables/Tables/CompressComplex.h000066400000000000000000000003651321422335000212550ustar00rootroot00000000000000#ifndef TABLES_TABLES_STUB_COMPRESSCOMPLEX_H #define TABLES_TABLES_STUB_COMPRESSCOMPLEX_H #include #warning "Tables/CompressComplex.h has been deprecated; use DataMan/CompressComplex.h instead" #endif casacore-2.4.1/tables/Tables/CompressFloat.h000066400000000000000000000003531321422335000207100ustar00rootroot00000000000000#ifndef TABLES_TABLES_STUB_COMPRESSFLOAT_H #define TABLES_TABLES_STUB_COMPRESSFLOAT_H #include #warning "Tables/CompressFloat.h has been deprecated; use DataMan/CompressFloat.h instead" #endif casacore-2.4.1/tables/Tables/ConcatColumn.cc000066400000000000000000000300661321422335000206560ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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; } uInt ConcatColumn::nrow() const { return refTabPtr_p->nrow(); } void ConcatColumn::initialize (uInt startRow, uInt endRow) { uInt tableNr, tabRownr; for (uInt i=startRow; irows().mapRownr (tableNr, tabRownr, i); refColPtr_p[tableNr]->initialize (tabRownr, tabRownr); } } void ConcatColumn::setShape (uInt rownr, const IPosition& shape) { uInt tableNr, tabRownr; refTabPtr_p->rows().mapRownr (tableNr, tabRownr, rownr); refColPtr_p[tableNr]->setShape (tabRownr, shape); } void ConcatColumn::setShape (uInt rownr, const IPosition& shape, const IPosition& tileShape) { uInt tableNr, 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 (uInt rownr) const { uInt tableNr, tabRownr; refTabPtr_p->rows().mapRownr (tableNr, tabRownr, rownr); return refColPtr_p[tableNr]->ndim (tabRownr); } IPosition ConcatColumn::shape(uInt rownr) const { uInt tableNr, tabRownr; refTabPtr_p->rows().mapRownr (tableNr, tabRownr, rownr); return refColPtr_p[tableNr]->shape (tabRownr); } Bool ConcatColumn::isDefined (uInt rownr) const { uInt tableNr, tabRownr; refTabPtr_p->rows().mapRownr (tableNr, tabRownr, rownr); return refColPtr_p[tableNr]->isDefined (tabRownr); } Bool ConcatColumn::canAccessScalarColumn (Bool& reask) const { return refColPtr_p[0]->canAccessScalarColumn (reask); } Bool ConcatColumn::canAccessScalarColumnCells (Bool& reask) const { return refColPtr_p[0]->canAccessScalarColumnCells (reask); } Bool ConcatColumn::canAccessArrayColumn (Bool& reask) const { return refColPtr_p[0]->canAccessArrayColumn (reask); } Bool ConcatColumn::canAccessArrayColumnCells (Bool& reask) const { return refColPtr_p[0]->canAccessArrayColumnCells (reask); } Bool ConcatColumn::canAccessSlice (Bool& reask) const { return refColPtr_p[0]->canAccessSlice (reask); } Bool ConcatColumn::canAccessColumnSlice (Bool& reask) const { return refColPtr_p[0]->canAccessColumnSlice (reask); } Bool ConcatColumn::canChangeShape() const { return refColPtr_p[0]->canChangeShape(); } void ConcatColumn::get (uInt rownr, void* dataPtr) const { uInt tableNr, 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::getSlice (uInt rownr, const Slicer& ns, void* dataPtr) const { uInt tableNr, tabRownr; refTabPtr_p->rows().mapRownr (tableNr, tabRownr, rownr); refColPtr_p[tableNr]->getSlice (tabRownr, ns, dataPtr); } void ConcatColumn::put (uInt rownr, const void* dataPtr) { uInt tableNr, 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::putSlice (uInt rownr, const Slicer& ns, const void* dataPtr) { uInt tableNr, tabRownr; refTabPtr_p->rows().mapRownr (tableNr, tabRownr, rownr); refColPtr_p[tableNr]->putSlice (tabRownr, ns, dataPtr); } void ConcatColumn::setMaximumCacheSize (uInt nbytes) { for (uInt i=0; isetMaximumCacheSize (nbytes); } } void ConcatColumn::allocIterBuf (void*& lastVal, void*& curVal, CountedPtr& cmpObj) { refColPtr_p[0]->allocIterBuf (lastVal, curVal, cmpObj); } void ConcatColumn::freeIterBuf (void*& lastVal, void*& curVal) { refColPtr_p[0]->freeIterBuf (lastVal, curVal); } void ConcatColumn::getArrayColumn (void* dataPtr) const { accessColumn (0, dataPtr, &getColumnPart); } void ConcatColumn::getColumnSlice (const Slicer& ns, void* dataPtr) const { accessColumn (&ns, dataPtr, &getColumnSlicePart); } void ConcatColumn::getArrayColumnCells (const RefRows& rownrs, void* dataPtr) const { accessRows (rownrs, 0, dataPtr, &getRowsPart); } void ConcatColumn::getColumnSliceCells (const RefRows& rownrs, const Slicer& ns, void* dataPtr) const { accessRows (rownrs, &ns, dataPtr, &getRowsSlicePart); } void ConcatColumn::putArrayColumn (const void* dataPtr) { accessColumn (0, const_cast(dataPtr), &putColumnPart); } void ConcatColumn::putColumnSlice (const Slicer& ns, const void* dataPtr) { accessColumn (&ns, const_cast(dataPtr), &putColumnSlicePart); } void ConcatColumn::putArrayColumnCells (const RefRows& rownrs, const void* dataPtr) { accessRows (rownrs, 0, const_cast(dataPtr), &putRowsPart); } void ConcatColumn::putColumnSliceCells (const RefRows& rownrs, const Slicer& ns, const void* dataPtr) { accessRows (rownrs, &ns, const_cast(dataPtr), &putRowsSlicePart); } void ConcatColumn::accessColumn (const Slicer* ns, void* dataPtr, AccessColumnFunc* accessFunc) const { ArrayBase& arr = *static_cast(dataPtr); IPosition st(arr.ndim(), 0); IPosition sz(arr.shape()); uInt nlast = arr.ndim() - 1; CountedPtr part; for (uInt i=0; inrow(); sz[nlast] = nr; // Store in CountedPtr, so deleted in case of exception. part = arr.getSection (Slicer(st, sz)); accessFunc (refColPtr_p[i], ns, part.operator->()); st[nlast] += nr; } } void ConcatColumn::accessRows (const RefRows& rownrs, const Slicer* ns, void* dataPtr, AccessRowsFunc* accessFunc) const { ArrayBase& arr = *static_cast(dataPtr); // 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 holder for the array part to handle. CountedPtr part; // 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 (uInt i=0; i= 0) { uInt nrrow = i - st[rowAxis]; sz[rowAxis] = nrrow; Vector rowPart(tabRowNrs(Slice(st[rowAxis], nrrow))); part = arr.getSection (Slicer(st, sz)); accessFunc (refColPtr_p[lastTabNr], RefRows(rowPart), ns, part.operator->()); } st[rowAxis] = i; lastTabNr = tableNr; } } if (lastTabNr >= 0) { uInt nrrow = rows.nelements() - st[rowAxis]; sz[rowAxis] = nrrow; Vector rowPart(tabRowNrs(Slice(st[rowAxis], nrrow))); part = arr.getSection (Slicer(st, sz)); accessFunc (refColPtr_p[lastTabNr], RefRows(rowPart), ns, part.operator->()); } } 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-2.4.1/tables/Tables/ConcatColumn.h000066400000000000000000000262251321422335000205220ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef TABLES_CONCATCOLUMN_H #define TABLES_CONCATCOLUMN_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class ConcatTable; class BaseColumnDesc; class TableRecord; class Slicer; class IPosition; template class Vector; //

        // 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 uInt nrow() const; // Test if a value in a particular cell has been defined. virtual Bool isDefined (uInt rownr) const; // Set the shape of the array in the given row. virtual void setShape (uInt rownr, const IPosition& shape); // Set the shape and tile shape of the array in the given row. virtual void setShape (uInt 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 (uInt rownr) const; // Get the shape of an array in a particular cell. virtual IPosition shape (uInt rownr) const; // It can change shape if the underlying column can. virtual Bool canChangeShape() const; // It can handle a scalar column if the underlying column // can handle cells in a scalar column. virtual Bool canAccessScalarColumn (Bool& reask) const; // It can handle an array column if the underlying column // can handle cells in an array column. virtual Bool canAccessArrayColumn (Bool& reask) const; // It can handle a cell slice if the underlying column can do it. virtual Bool canAccessSlice (Bool& reask) const; // It can handle a column slice if the underlying column // can handle a collection of cells in a column and a column slice. virtual Bool canAccessColumnSlice (Bool& reask) const; // It can handle cells in a scalar column if the underlying column // can do it. virtual Bool canAccessScalarColumnCells (Bool& reask) const; // It can handle cells in an array column if the underlying column // can do it. virtual Bool canAccessArrayColumnCells (Bool& reask) const; // Initialize the rows from startRownr till endRownr (inclusive) // with the default value defined in the column description (if defined). void initialize (uInt startRownr, uInt endRownr); // Get the value from a particular cell. // This can be a scalar or an array. virtual void get (uInt rownr, void* dataPtr) const; // Get a slice of an N-dimensional array in a particular cell. virtual void getSlice (uInt rownr, const Slicer&, void* dataPtr) const; // Put the value in a particular cell. // This can be a scalar or an array. virtual void put (uInt rownr, const void* dataPtr); // Put a slice of an N-dimensional array in a particular cell. virtual void putSlice (uInt rownr, const Slicer&, const void* dataPtr); // 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 (void* 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&, void* 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, void* 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&, void* dataPtr) const; // 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 void* 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 void* 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 void* 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 void* 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, CountedPtr& 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, void* dataPtr, AccessColumnFunc*) const; // Access the data with multiple rows combined. void accessRows (const RefRows& rownrs, const Slicer* ns, void* 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-2.4.1/tables/Tables/ConcatRows.cc000066400000000000000000000071251321422335000203530ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN void ConcatRows::add (uInt 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 (uInt 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 && inx0 ? rows[0]-1 : 0; itsChunk[2] = 1; } // Construct the iterator on a ConcatRows object for the given row range. ConcatRowsIter::ConcatRowsIter (const ConcatRows& rows, uInt start, uInt end, uInt incr) : itsRows (&rows), itsChunk (3), itsStart (start), itsEnd (std::min(end+1, rows.nrow())), itsIncr (incr), itsPos (0) { if (itsStart >= itsEnd) { itsPastEnd = True; } else { itsPastEnd = False; rows.mapRownr (itsPos, itsChunk[0], start); itsChunk[1] = std::min(rows[itsPos], itsEnd) - 1 - rows[itsPos-1]; itsChunk[2] = itsIncr; } } void ConcatRowsIter::next() { if (!itsPastEnd) { if (itsPos+1 >= itsRows->ntable() || (*itsRows)[itsPos] >= itsEnd) { itsPastEnd = True; } else { itsChunk[0] = 0; if (itsIncr != 1) { uInt rem = ((*itsRows)[itsPos] - itsStart) % itsIncr; if (rem != 0) { itsChunk[0] = itsIncr - rem; } } itsChunk[1] = std::min((*itsRows)[itsPos+1], itsEnd) - 1 - (*itsRows)[itsPos]; ++itsPos; } } } } //# NAMESPACE CASACORE - END casacore-2.4.1/tables/Tables/ConcatRows.h000066400000000000000000000163061321422335000202160ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (uInt nrow); // Give the nr of tables. uInt ntable() const { return itsNTable; } // Get the total nr of rows. uInt nrow() const { return itsRows[itsNTable]; } // Give the nr of rows for the i-th table. uInt operator[] (uInt i) const { return itsRows[i+1]; } // Give the offset for the i-th table. uInt offset (uInt i) const { return itsRows[i]; } // Map an overall row number to a table and row number. void mapRownr (uInt& tableNr, uInt& tabRownr, uInt 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 (uInt rownr) const; //# Data members. Block itsRows; uInt itsNTable; mutable uInt itsLastStRow; //# Cached variables to spped up mutable uInt 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. // uInt rownr = rowiter.sliceStart(); // uInt end = rowiter.sliceEnd(); // uInt 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&, uInt start, uInt end, uInt 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 itsPos; } private: const ConcatRows* itsRows; Vector itsChunk; uInt itsStart; uInt itsEnd; uInt itsIncr; uInt itsPos; Bool itsPastEnd; }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/tables/Tables/ConcatScalarColumn.h000066400000000000000000000107541321422335000216500ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 referenced column. ConcatScalarColumn (const BaseColumnDesc*, ConcatTable*); ~ConcatScalarColumn(); // Get the vector of all scalar values in a column. virtual void getScalarColumn (void* dataPtr) const; // Get the vector of some scalar values in a column. virtual void getScalarColumnCells (const RefRows& rownrs, void* dataPtr) const; // Put the vector of all scalar values in the column. virtual void putScalarColumn (const void* dataPtr); // Get the vector of some scalar values in a column. virtual void putScalarColumnCells (const RefRows& rownrs, const void* dataPtr); // Handle the creation and deletion of sort keys. // virtual void makeSortKey (Sort& sortobj, CountedPtr& cmpObj, Int order, const void*& dataSave); virtual void makeRefSortKey (Sort& sortobj, CountedPtr& cmpObj, Int order, const Vector& rownrs, const void*& dataSave); virtual void fillSortKey (const Vector* vecPtr, Sort& sortobj, CountedPtr& cmpObj, Int order); virtual void freeSortKey (const void*& dataSave); // }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-2.4.1/tables/Tables/ConcatScalarColumn.tcc000066400000000000000000000152241321422335000221670ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (void* dataPtr) const { Vector& vec = *static_cast*>(dataPtr); uInt 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, void* dataPtr) const { Vector& vec = *static_cast*>(dataPtr); // 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(); uInt tabRownr; uInt tableNr=0; // Map each row to rownr and tablenr. // Note this is pretty fast because it is done in row order. for (uInt 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 void* dataPtr) { Vector vec (*static_cast*>(dataPtr)); uInt 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 void* dataPtr) { const Vector& vec = *static_cast*>(dataPtr); // 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(); uInt tabRownr; uInt tableNr=0; // Map each row to rownr and tablenr. // Note this is pretty fast because it is done in row order. for (uInt 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, CountedPtr& cmpObj, Int order, const void*& dataSave) { //# Get the data as a column. //# Save the pointer to the vector for deletion by freeSortKey(). dataSave = 0; ScalarColumn col(refTabPtr_p->asTable(), this->columnDesc().name()); Vector* vecPtr = new Vector; col.getColumn (*vecPtr); dataSave = vecPtr; fillSortKey (vecPtr, sortobj, cmpObj, order); } template void ConcatScalarColumn::makeRefSortKey (Sort& sortobj, CountedPtr& cmpObj, Int order, const Vector& rownrs, const void*& dataSave) { //# Get the data as a column. dataSave = 0; ScalarColumn col(refTabPtr_p->asTable(), this->columnDesc().name()); Vector* vecPtr = new Vector; col.getColumnCells (RefRows(rownrs), *vecPtr); dataSave = vecPtr; fillSortKey (vecPtr, sortobj, cmpObj, order); } template void ConcatScalarColumn::fillSortKey (const Vector* vecPtr, Sort& sortobj, CountedPtr& 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.null()) { cmpObj = new ObjCompare(); } sortobj.sortKey (datap, cmpObj, sizeof(T), order == Sort::Descending ? Sort::Descending : Sort::Ascending); vecPtr->freeStorage (datap, deleteIt); } template void ConcatScalarColumn::freeSortKey (const void*& dataSave) { if (dataSave != 0) { delete (Vector*)dataSave; } dataSave = 0; } } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/tables/Tables/ConcatTable.cc000066400000000000000000000457601321422335000204570ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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, uInt nrrow, int option, const TableLock& lockOptions, const TSMOption& tsmOption) : BaseTable (name, option, nrrow), colMap_p (static_cast(0)), 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), colMap_p (static_cast(0)), changed_p (True) { noWrite_p = True; if (tables.nelements() == 0) { throw TableError("ConcatTable: at least one table has to be given"); } baseTabPtr_p.resize (tables.nelements()); baseTabPtr_p = 0; rows_p.reserve (tables.nelements() + 1); for (uInt i=0; ilink(); rows_p.add (baseTabPtr_p[i]->nrow()); } nrrow_p = rows_p.nrow(); initialize(); addInfo(); noWrite_p = False; } ConcatTable::ConcatTable (const Block& 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), colMap_p (static_cast(0)), changed_p (True) { noWrite_p = True; if (tableNames.nelements() == 0) { throw TableError("ConcatTable: at least one table has to be given"); } openTables (tableNames, option, lockOptions, tsmOption); initialize(); addInfo(); noWrite_p = False; } ConcatTable::~ConcatTable() { //# When needed, write the table files if not marked for delete if (!isMarkedForDelete()) { if (openedForWrite() && !shouldNotWrite()) { writeConcatTable (True); } } //# Delete all ConcatColumn objects. for (uInt i=0; itableInfo(); // Add a line for each table. tableInfo().readmeAddLine ("Virtual concatenation of the following tables:"); for (uInt i=0; itableName()); } else { tableInfo().readmeAddLine (" " + subDirName_p + "/" + Path(baseTabPtr_p[i]->tableName()).baseName()); } } } void ConcatTable::getPartNames (Block& names, Bool recursive) const { if (recursive) { for (uInt i=0; igetPartNames (names, recursive); } } else { uInt inx = names.size(); names.resize (inx + baseTabPtr_p.nelements()); for (uInt i=0; itableName(); } } } void ConcatTable::reopenRW() { for (uInt i=0; ireopenRW(); } option_p = Table::Update; } Bool ConcatTable::asBigEndian() const { return baseTabPtr_p[0]->asBigEndian(); } const StorageOption& ConcatTable::storageOption() const { return baseTabPtr_p[0]->storageOption(); } Bool ConcatTable::isMultiUsed (Bool) const { return False; } const TableLock& ConcatTable::lockOptions() const { return baseTabPtr_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; ihasLock (type)) { return False; } } return True; } Bool ConcatTable::lock (FileLocker::LockType type, uInt nattempts) { for (uInt i=0; ilock (type, nattempts)) { return False; } } return True; } void ConcatTable::unlock() { for (uInt i=0; iunlock(); } } void ConcatTable::flush (Bool fsync, Bool recursive) { // Flush the underlying table. for (uInt i=0; iflush (fsync, recursive); } if (!isMarkedForDelete()) { if (openedForWrite()) { writeConcatTable (fsync); } } } void ConcatTable::resync() { for (uInt i=0; iresync(); } } uInt ConcatTable::getModifyCounter() const { return baseTabPtr_p[0]->getModifyCounter(); } //# 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(baseTabPtr_p.nelements()); for (uInt i=0; irename (sdName + Path(baseTabPtr_p[i]->tableName()).baseName(), Table::New); } ios << Path::stripDirectory (baseTabPtr_p[i]->tableName(), tableName()); } // Write the names to be concatenated. ios << subTableNames_p; ios.putend(); writeEnd (ios); changed_p = False; } //# Write the TableInfo. flushTableInfo(); } //# Read a concatenate table from a file and open the associated tables. void ConcatTable::getConcat (AipsIO& ios, int option, const TableLock& lockOptions, const TSMOption& tsmOption) { //# Open the file, read name and type of root and read object data. uInt nrtab; Block 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. baseTabPtr_p.resize (tableNames.nelements()); baseTabPtr_p = 0; rows_p.reserve (tableNames.nelements() + 1); for (uInt i=0; ilink(); rows_p.add (tab.nrow()); } nrrow_p = rows_p.nrow(); } void ConcatTable::initialize() { // Check if all tables have the same description. // Note that we size the table instead of reserve, because push_back // gives the following warning for CountedPtr: // "dereferencing pointer aonymous does break strict-aliasing rule" vector > actualDesc(baseTabPtr_p.nelements());; Bool equalDataTypes; for (uInt i=0; i (new TableDesc (baseTabPtr_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 = new TableDesc (*(actualDesc[0]), TableDesc::Scratch); keywordSet_p = baseTabPtr_p[0]->keywordSet(); // Create the concatColumns. makeConcatCol(); // Handle the possible concatenated subtables. handleSubTables(); } void ConcatTable::handleSubTables() { // Check for each subtable if it exists in all tables. // If fine, create a ConcatTable for each subtable. Block subtables(baseTabPtr_p.nelements()); for (uInt i=0; ikeywordSet().asTable (tname); } Table concSubtab(subtables); keywordSet_p.defineTable (tname, concSubtab); } } //# Read description and #rows. void ConcatTable::getLayout (TableDesc& desc, AipsIO& ios) { //# Open the file, read name and type of root and read object data. uInt nrtab; Block 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(); Table::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.define (cd.name(), cd.makeConcatColumn (this)); } } Block ConcatTable::getRefColumns (const String& columnName) { Block cols(baseTabPtr_p.nelements()); for (uInt i=0; igetColumn (columnName); } return cols; } //# Test if the table is writable. Bool ConcatTable::isWritable() const { for (uInt i=0; iisWritable()) { return False; } } return True; } void ConcatTable::copy (const String& newName, int tableOption) const { if (!madeDir_p) { throw TableError ("ConcatTable::copy: an unsaved table cannot be shallowly copied; " "make a deep copy or save the table first"); } BaseTable::copy (newName, tableOption); } void ConcatTable::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 ConcatTable::tableType() const { return baseTabPtr_p[0]->tableType(); } TableDesc ConcatTable::actualTableDesc() const { return *tdescPtr_p; } Record ConcatTable::dataManagerInfo() const { return baseTabPtr_p[0]->dataManagerInfo(); } //# Get the keyword set. TableRecord& ConcatTable::keywordSet() { return keywordSet_p; } //# Get the keyword set. TableRecord& ConcatTable::rwKeywordSet() { return keywordSet_p; } BaseColumn* ConcatTable::getColumn (const String& columnName) const { tdescPtr_p->columnDesc(columnName); // check if column exists return colMap_p(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* ConcatTable::getColumn (uInt columnIndex) const { const String& name = tdescPtr_p->columnDesc(columnIndex).name(); return colMap_p(name); } void ConcatTable::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 (!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; iaddColumn (columnDesc, addToParent); } tdescPtr_p->addColumn (columnDesc); } void ConcatTable::addColumn (const ColumnDesc& columnDesc, const String& dataManager, Bool byName, Bool addToParent) { checkAddColumn (columnDesc.name(), addToParent); for (uInt i=0; iaddColumn (columnDesc, dataManager, byName, addToParent); } tdescPtr_p->addColumn (columnDesc); } void ConcatTable::addColumn (const ColumnDesc& columnDesc, const DataManager& dataManager, Bool addToParent) { checkAddColumn (columnDesc.name(), addToParent); for (uInt i=0; iaddColumn (columnDesc,dataManager, addToParent); } tdescPtr_p->addColumn (columnDesc); } void ConcatTable::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. for (uInt i=0; iaddColumn (tableDesc, dataManager, addToParent); } for (uInt i=0; iaddColumn (tableDesc[i]); } } //# Rows and columns cannot be removed and renamed. Bool ConcatTable::canRemoveRow() const { return False; } Bool ConcatTable::canRemoveColumn (const Vector&) const { return False; } Bool ConcatTable::canRenameColumn (const String&) const { return False; } void ConcatTable::removeRow (uInt) { 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 baseTabPtr_p[0]->findDataManager (name, byColumn); } void ConcatTable::showStructureExtra (std::ostream& os) const { for (uInt i=0; itableName() << " (" << baseTabPtr_p[i]->nrow() << " rows, " << baseTabPtr_p[i]->tableDesc().ncolumn() << " columns)" << endl; } } } //# NAMESPACE CASACORE - END casacore-2.4.1/tables/Tables/ConcatTable.h000066400000000000000000000345341321422335000203160ustar00rootroot00000000000000//# ConcatTable.h: 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef TABLES_CONCATTABLE_H #define TABLES_CONCATTABLE_H //# Includes #include #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 b 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, uInt 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(); // 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 (uInt 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); // Create a (temporary) Table object from it. Table asTable() { return Table (this, False); } private: // Copy constructor is forbidden, because copying a table requires // some more knowledge (like table name of result). // Declaring it private, makes it unusable. ConcatTable (const ConcatTable&); // Assignment is forbidden, because copying a table requires // some more knowledge (like table name of result). // Declaring it private, makes it unusable. ConcatTable& operator= (const ConcatTable&); // 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); //# Data members Block subTableNames_p; String subDirName_p; Block baseTabPtr_p; //# pointers to parent tables SimpleOrderedMap 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-2.4.1/tables/Tables/DataManAccessor.h000066400000000000000000000003651321422335000211220ustar00rootroot00000000000000#ifndef TABLES_TABLES_STUB_DATAMANACCESSOR_H #define TABLES_TABLES_STUB_DATAMANACCESSOR_H #include #warning "Tables/DataManAccessor.h has been deprecated; use DataMan/DataManAccessor.h instead" #endif casacore-2.4.1/tables/Tables/DataManError.h000066400000000000000000000003461321422335000204500ustar00rootroot00000000000000#ifndef TABLES_TABLES_STUB_DATAMANERROR_H #define TABLES_TABLES_STUB_DATAMANERROR_H #include #warning "Tables/DataManError.h has been deprecated; use DataMan/DataManError.h instead" #endif casacore-2.4.1/tables/Tables/DataManager.h000066400000000000000000000003411321422335000202700ustar00rootroot00000000000000#ifndef TABLES_TABLES_STUB_DATAMANAGER_H #define TABLES_TABLES_STUB_DATAMANAGER_H #include #warning "Tables/DataManager.h has been deprecated; use DataMan/DataManager.h instead" #endif casacore-2.4.1/tables/Tables/ExprNode.h000066400000000000000000000003141321422335000176500ustar00rootroot00000000000000#ifndef TABLES_TABLES_STUB_EXPRNODE_H #define TABLES_TABLES_STUB_EXPRNODE_H #include #warning "Tables/ExprNode.h has been deprecated; use TaQL/ExprNode.h instead" #endif casacore-2.4.1/tables/Tables/ExprNodeSet.h000066400000000000000000000003331321422335000203250ustar00rootroot00000000000000#ifndef TABLES_TABLES_STUB_EXPRNODESET_H #define TABLES_TABLES_STUB_EXPRNODESET_H #include #warning "Tables/ExprNodeSet.h has been deprecated; use TaQL/ExprNodeSet.h instead" #endif casacore-2.4.1/tables/Tables/ExternalLockSync.cc000066400000000000000000000042461321422335000215220ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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-2.4.1/tables/Tables/ExternalLockSync.h000066400000000000000000000113041321422335000213550ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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(); // 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. uInt 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 (uInt 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: // Copy constructor is forbidden. ExternalLockSync (const ExternalLockSync& that); // Assignment is forbidden. ExternalLockSync& operator= (const ExternalLockSync& that); // 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; uInt itsNrrow; }; inline Bool ExternalLockSync::hasLock (FileLocker::LockType type) const { return itsLock.hasLock (type); } inline void ExternalLockSync::release (uInt nrrow) { itsNrrow = nrrow; itsLock.release(); } inline MemoryIO* ExternalLockSync::doReleaseCallBack (Bool) { itsSync.write (itsNrrow); return &(itsSync.memoryIO()); } inline uInt ExternalLockSync::nrow() const { return itsNrrow; } } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/tables/Tables/ForwardCol.h000066400000000000000000000003341321422335000201700ustar00rootroot00000000000000#ifndef TABLES_TABLES_STUB_FORWARDCOL_H #define TABLES_TABLES_STUB_FORWARDCOL_H #include #warning "Tables/ForwardCol.h has been deprecated; use DataMan/ForwardCol.h instead" #endif casacore-2.4.1/tables/Tables/ForwardColRow.h000066400000000000000000000003531321422335000206610ustar00rootroot00000000000000#ifndef TABLES_TABLES_STUB_FORWARDCOLROW_H #define TABLES_TABLES_STUB_FORWARDCOLROW_H #include #warning "Tables/ForwardColRow.h has been deprecated; use DataMan/ForwardColRow.h instead" #endif casacore-2.4.1/tables/Tables/IncrStManAccessor.h000066400000000000000000000003771321422335000214560ustar00rootroot00000000000000#ifndef TABLES_TABLES_STUB_INCRSTMANACCESSOR_H #define TABLES_TABLES_STUB_INCRSTMANACCESSOR_H #include #warning "Tables/IncrStManAccessor.h has been deprecated; use DataMan/IncrStManAccessor.h instead" #endif casacore-2.4.1/tables/Tables/IncrementalStMan.h000066400000000000000000000003721321422335000213340ustar00rootroot00000000000000#ifndef TABLES_TABLES_STUB_INCREMENTALSTMAN_H #define TABLES_TABLES_STUB_INCREMENTALSTMAN_H #include #warning "Tables/IncrementalStMan.h has been deprecated; use DataMan/IncrementalStMan.h instead" #endif casacore-2.4.1/tables/Tables/MemoryTable.cc000066400000000000000000000216441321422335000205130ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN MemoryTable::MemoryTable (SetupNewTable& newtab, uInt 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. TableDesc* tdescPtr = newtab.tableDescPtr(); ColumnSet* 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, False); 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; delete colSetPtr_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 (uInt 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 (uInt rownr) { colSetPtr_p->removeRow (rownr); nrrow_p--; } void MemoryTable::addColumn (const ColumnDesc& columnDesc, Bool) { Table tab(this, False); 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, False); 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, False); // 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, False); // 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-2.4.1/tables/Tables/MemoryTable.h000066400000000000000000000207301321422335000203500ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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&, uInt nrrow, Bool initialize); // The destructor deletes all data. virtual ~MemoryTable(); // 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 (uInt 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 (uInt 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: ColumnSet* colSetPtr_p; //# pointer to set of columns TableLockData* lockPtr_p; //# pointer to lock object // Copy constructor is forbidden, because copying a table requires // some more knowledge (like table name of result). // Declaring it private, makes it unusable. MemoryTable (const MemoryTable&); // Assignment is forbidden, because copying a table requires // some more knowledge (like table name of result). // Declaring it private, makes it unusable. MemoryTable& operator= (const MemoryTable&); // 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-2.4.1/tables/Tables/NullTable.cc000066400000000000000000000136641321422335000201600ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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() { throwError ("reopenRW"); } Bool NullTable::asBigEndian() const { throwError ("asBigEndian"); return True; } Bool NullTable::isMultiUsed (Bool) const { throwError ("isMultiUsed"); return False; } const StorageOption& NullTable::storageOption() const { throwError ("storageOption"); return storageOption(); // to satisfy compiler } const TableLock& NullTable::lockOptions() const { throwError ("lockOptions"); return lockOptions(); // to satisfy compiler } void NullTable::mergeLock (const TableLock&) { throwError ("mergeLoc"); } Bool NullTable::hasLock (FileLocker::LockType) const { throwError ("hasLock"); return False; } Bool NullTable::lock (FileLocker::LockType, uInt) { throwError ("lock"); return False; } void NullTable::unlock() { throwError ("unlock"); } void NullTable::flush (Bool, Bool) { throwError ("flush"); } void NullTable::resync() { throwError ("resync"); } uInt NullTable::getModifyCounter() const { throwError ("getModifyCounter"); return 0; } Bool NullTable::isWritable() const { throwError ("isWritable"); return False; } void NullTable::deepCopy (const String&, const Record&, const StorageOption&, int, Bool, int, Bool) const { throwError ("deepCopy"); } TableDesc NullTable::actualTableDesc() const { throwError ("actualTableDesc"); return actualTableDesc(); // to satisfy compiler } Record NullTable::dataManagerInfo() const { throwError ("dataManagerInfo"); return dataManagerInfo(); // to satisfy compiler } TableRecord& NullTable::keywordSet() { throwError ("keywordSet"); return keywordSet(); // to satisfy compiler } TableRecord& NullTable::rwKeywordSet() { throwError ("rwKeywordSet"); return rwKeywordSet(); // to satisfy compiler } BaseColumn* NullTable::getColumn (uInt) const { throwError ("getColumn"); return 0; } BaseColumn* NullTable::getColumn (const String&) const { throwError ("getColumn"); return 0; } Bool NullTable::canAddRow() const { throwError ("canAddRow"); return False; } void NullTable::addRow (uInt, Bool) { throwError ("addRow"); } Bool NullTable::canRemoveRow() const { throwError ("canRemoveRow"); return False; } void NullTable::removeRow (uInt) { throwError ("removeRow"); } DataManager* NullTable::findDataManager (const String&, Bool) const { throwError ("findDataManager"); return 0; } void NullTable::addColumn (const ColumnDesc&, Bool) { throwError ("addColumn"); } void NullTable::addColumn (const ColumnDesc&, const String&, Bool, Bool) { throwError ("addColumn"); } void NullTable::addColumn (const ColumnDesc&, const DataManager&, Bool) { throwError ("addColumn"); } void NullTable::addColumn (const TableDesc& , const DataManager&, Bool) { throwError ("addColumn"); } Bool NullTable::canRemoveColumn (const Vector&) const { throwError ("canRemoveColumn"); return False; } void NullTable::removeColumn (const Vector&) { throwError ("removeColumn"); } Bool NullTable::canRenameColumn (const String&) const { throwError ("canRenameColumn"); return False; } void NullTable::renameColumn (const String&, const String&) { throwError ("renameColumn"); } void NullTable::renameHypercolumn (const String&, const String&) { throwError ("renameHypercolumn"); } Vector NullTable::rowNumbers() const { throwError ("rowNumbers"); return Vector(); } BaseTable* NullTable::root() { throwError ("root"); return 0; } Bool NullTable::rowOrder() const { throwError ("rowOrde"); return False; } Vector* NullTable::rowStorage() { throwError ("rowStorage"); return 0; } Bool NullTable::adjustRownrs (uInt, Vector&, Bool) const { throwError ("adjustRownrs"); return False; } BaseTable* NullTable::doSort (PtrBlock&, const Block >&, const Block&, int) { throwError ("doSort"); return 0; } void NullTable::renameSubTables (const String&, const String&) { throwError ("renameSubTables"); } void NullTable::throwError (const String& name) const { throw TableError ("NullTable::" + name + " - Table object is empty"); } } //# NAMESPACE CASACORE - END casacore-2.4.1/tables/Tables/NullTable.h000066400000000000000000000131001321422335000200030ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef TABLES_NULLTABLE_H #define TABLES_NULLTABLE_H //# Includes #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.. // // // Nullable is used to represent a null table. // The default Table constructor used to a create a null pointer // which results in core dumps when the Table object is 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(); // The table is a null table. virtual Bool isNull() const; // All functions throw a "null table" exception. // virtual void reopenRW(); virtual Bool asBigEndian() const; virtual const StorageOption& storageOption() const; virtual Bool isMultiUsed (Bool checkSubTable) const; virtual const TableLock& lockOptions() const; virtual void mergeLock (const TableLock& lockOptions); virtual Bool hasLock (FileLocker::LockType) const; virtual Bool lock (FileLocker::LockType, uInt nattempts); virtual void unlock(); virtual void flush (Bool fsync, Bool recursive); virtual void resync(); virtual uInt getModifyCounter() const; virtual Bool isWritable() const; virtual void deepCopy (const String& newName, const Record& dataManagerInfo, const StorageOption&, int tableOption, Bool valueCopy, int endianFormat, Bool noRows) const; virtual TableDesc actualTableDesc() const; virtual Record dataManagerInfo() const; virtual TableRecord& keywordSet(); virtual TableRecord& rwKeywordSet(); virtual BaseColumn* getColumn (uInt columnIndex) const; virtual BaseColumn* getColumn (const String& columnName) const; virtual Bool canAddRow() const; virtual void addRow (uInt nrrow, Bool initialize); virtual Bool canRemoveRow() const; virtual void removeRow (uInt rownr); virtual DataManager* findDataManager (const String& name, Bool byColumn) const; 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); virtual Bool canRemoveColumn (const Vector& columnNames) const; virtual void removeColumn (const Vector& columnNames); virtual Bool canRenameColumn (const String& columnName) const; virtual void renameColumn (const String& newName, const String& oldName); virtual void renameHypercolumn (const String& newName, const String& oldName); virtual Vector rowNumbers() const; virtual BaseTable* root(); virtual Bool rowOrder() const; virtual Vector* rowStorage(); virtual Bool adjustRownrs (uInt nrrow, Vector& rownrs, Bool determineOrder) const; virtual BaseTable* doSort (PtrBlock&, const Block >&, const Block& sortOrder, int sortOption); virtual void renameSubTables (const String& newName, const String& oldName); // private: // Copy constructor is forbidden, because copying a table requires // some more knowledge (like table name of result). // Declaring it private, makes it unusable. NullTable (const NullTable&); // Assignment is forbidden, because copying a table requires // some more knowledge (like table name of result). // Declaring it private, makes it unusable. NullTable& operator= (const NullTable&); // Throw an exception with the name of the function. void throwError (const String& name) const; }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/tables/Tables/PlainColumn.cc000066400000000000000000000121101321422335000205000ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (colDesc_p); rtraceColumn_p = (trace&TableTrace::READ) != 0; wtraceColumn_p = (trace&TableTrace::WRITE) != 0; } PlainColumn::~PlainColumn() {} uInt PlainColumn:: nrow() const { return colSetPtr_p->nrow(); } TableRecord& PlainColumn::rwKeywordSet() { Bool hasLocked = colSetPtr_p->userLock (FileLocker::Write, True); checkWriteLock (True); TableRecord& rec = colDesc_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 = colDesc_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 " + columnDesc().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 = columnDesc().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 = columnDesc().maxLength(); if (maxlen == 0) { return; } ReadOnlyArrayIterator iter (*value, 1); while (! iter.pastEnd()) { Vector vec (iter.array()); for (uInt i=0; i maxlen) { throw (TableError ("Scalar/ArrayColumn::put: string value '" + vec(i) + "' exceeds maximum length")); } } iter.next(); } } } //# NAMESPACE CASACORE - END casacore-2.4.1/tables/Tables/PlainColumn.h000066400000000000000000000147511321422335000203570ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef TABLES_PLAINCOLUMN_H #define TABLES_PLAINCOLUMN_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class TableAttr; class BaseColumnDesc; class DataManager; class DataManagerColumn; class AipsIO; template class Array; 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. uInt 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-2.4.1/tables/Tables/PlainTable.cc000066400000000000000000000615211321422335000203040ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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, uInt nrrow, Bool initialize, const TableLock& lockOptions, int endianFormat, const TSMOption& tsmOption) : BaseTable (newtab.name(), newtab.option(), 0), 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, False); 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 (AipsError&) { delete lockPtr_p; lockPtr_p = 0; delete colSetPtr_p; colSetPtr_p = 0; throw; } } PlainTable::PlainTable (AipsIO&, uInt version, const String& tabname, const String& type, uInt nrrow, int opt, const TableLock& lockOptions, const TSMOption& tsmOption, Bool addToCache, uInt locknr) : BaseTable (tabname, opt, nrrow), colSetPtr_p (0), 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 = new TableDesc ("", 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"); uInt format; ios >> nrrow; ios >> format; bigEndian_p = (format==0); ios >> tp; #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(), 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 = new ColumnSet (tdescPtr_p); colSetPtr_p->linkToTable (this); colSetPtr_p->linkToLockObject (lockPtr_p); if (version == 1) { 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, False); 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 (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 colSetPtr_p; 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; uInt 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. BaseTable* btab = Table::makeBaseTable (tableName(), "", Table::Old, TableLock(TableLock::PermanentLocking), TSMOption(TSMOption::Buffer,0,0), False, 1); PlainTable* tab = (PlainTable*)btab; 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; delete tab; } 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; uInt nrrow; if (! lockSync_p.read (nrrow, ncolumn, tableChanged, colSetPtr_p->dataManChanged())) { tableChanged = False; } else { 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 (uInt 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 (uInt 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, False); 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, False); 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, False); 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, False); 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", "big"); 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-2.4.1/tables/Tables/PlainTable.h000066400000000000000000000264411321422335000201500ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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. // // //# 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&, uInt 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, uInt 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(); // 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 (uInt 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 (uInt 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: // Copy constructor is forbidden, because copying a table requires // some more knowledge (like table name of result). // Declaring it private, makes it unusable. PlainTable (const PlainTable&); // Assignment is forbidden, because copying a table requires // some more knowledge (like table name of result). // Declaring it private, makes it unusable. PlainTable& operator= (const PlainTable&); // 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; ColumnSet* 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-2.4.1/tables/Tables/ReadAsciiTable.cc000066400000000000000000001310001321422335000210530ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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[16]; i++; sprintf (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, uInt 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, uInt 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-2.4.1/tables/Tables/ReadAsciiTable.h000066400000000000000000000444251321422335000207330ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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, uInt 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, uInt rownr); }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/tables/Tables/RecordGram.h000066400000000000000000000003261321422335000201540ustar00rootroot00000000000000#ifndef TABLES_TABLES_STUB_RECORDGRAM_H #define TABLES_TABLES_STUB_RECORDGRAM_H #include #warning "Tables/RecordGram.h has been deprecated; use TaQL/RecordGram.h instead" #endif casacore-2.4.1/tables/Tables/RefColumn.cc000066400000000000000000000163341321422335000201650ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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(); } uInt RefColumn::nrow() const { return refTabPtr_p->nrow(); } void RefColumn::initialize (uInt startRow, uInt endRow) { uInt rownr; for (uInt i=startRow; irootRownr(i); colPtr_p->initialize (rownr, rownr); } } void RefColumn::setShape (uInt rownr, const IPosition& shape) { colPtr_p->setShape (refTabPtr_p->rootRownr(rownr), shape); } void RefColumn::setShape (uInt 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 (uInt rownr) const { return colPtr_p->ndim (refTabPtr_p->rootRownr(rownr)); } IPosition RefColumn::shape(uInt rownr) const { return colPtr_p->shape (refTabPtr_p->rootRownr(rownr)); } Bool RefColumn::isDefined (uInt rownr) const { return colPtr_p->isDefined (refTabPtr_p->rootRownr(rownr)); } Bool RefColumn::canAccessScalarColumn (Bool& reask) const { return colPtr_p->canAccessScalarColumnCells (reask); } Bool RefColumn::canAccessScalarColumnCells (Bool& reask) const { return colPtr_p->canAccessScalarColumnCells (reask); } Bool RefColumn::canAccessArrayColumn (Bool& reask) const { return colPtr_p->canAccessArrayColumnCells (reask); } Bool RefColumn::canAccessArrayColumnCells (Bool& reask) const { return colPtr_p->canAccessArrayColumnCells (reask); } Bool RefColumn::canAccessSlice (Bool& reask) const { return colPtr_p->canAccessSlice (reask); } Bool RefColumn::canAccessColumnSlice (Bool& reask) const { Bool reask1; Bool acc1 = colPtr_p->canAccessColumnSlice (reask1); Bool acc2 = colPtr_p->canAccessArrayColumnCells (reask); if (reask1) { reask = reask1; } return (acc1 && acc2); } Bool RefColumn::canChangeShape() const { return colPtr_p->canChangeShape(); } void RefColumn::get (uInt rownr, void* dataPtr) const { colPtr_p->get (refTabPtr_p->rootRownr(rownr), dataPtr); } void RefColumn::getSlice (uInt rownr, const Slicer& ns, void* dataPtr) const { colPtr_p->getSlice (refTabPtr_p->rootRownr(rownr), ns, dataPtr); } void RefColumn::put (uInt rownr, const void* dataPtr) { colPtr_p->put (refTabPtr_p->rootRownr(rownr), dataPtr); } void RefColumn::putSlice (uInt rownr, const Slicer& ns, const void* dataPtr) { colPtr_p->putSlice (refTabPtr_p->rootRownr(rownr), ns, dataPtr); } void RefColumn::getScalarColumn (void* dataPtr) const { colPtr_p->getScalarColumnCells (refTabPtr_p->rowNumbers(), dataPtr); } void RefColumn::getArrayColumn (void* dataPtr) const { colPtr_p->getArrayColumnCells (refTabPtr_p->rowNumbers(), dataPtr); } void RefColumn::getColumnSlice (const Slicer& ns, void* dataPtr) const { colPtr_p->getColumnSliceCells (refTabPtr_p->rowNumbers(), ns, dataPtr); } void RefColumn::getScalarColumnCells (const RefRows& rownrs, void* dataPtr) const { colPtr_p->getScalarColumnCells (rownrs.convert(refTabPtr_p->rowNumbers()), dataPtr); } void RefColumn::getArrayColumnCells (const RefRows& rownrs, void* dataPtr) const { colPtr_p->getArrayColumnCells (rownrs.convert(refTabPtr_p->rowNumbers()), dataPtr); } void RefColumn::getColumnSliceCells (const RefRows& rownrs, const Slicer& ns, void* dataPtr) const { colPtr_p->getColumnSliceCells (rownrs.convert(refTabPtr_p->rowNumbers()), ns, dataPtr); } void RefColumn::putScalarColumn (const void* dataPtr) { colPtr_p->putScalarColumnCells (refTabPtr_p->rowNumbers(), dataPtr); } void RefColumn::putArrayColumn (const void* dataPtr) { colPtr_p->putArrayColumnCells (refTabPtr_p->rowNumbers(), dataPtr); } void RefColumn::putColumnSlice (const Slicer& ns, const void* dataPtr) { colPtr_p->putColumnSliceCells (refTabPtr_p->rowNumbers(), ns, dataPtr); } void RefColumn::putScalarColumnCells (const RefRows& rownrs, const void* dataPtr) { colPtr_p->putScalarColumnCells (rownrs.convert(refTabPtr_p->rowNumbers()), dataPtr); } void RefColumn::putArrayColumnCells (const RefRows& rownrs, const void* dataPtr) { colPtr_p->putArrayColumnCells (rownrs.convert(refTabPtr_p->rowNumbers()), dataPtr); } void RefColumn::putColumnSliceCells (const RefRows& rownrs, const Slicer& ns, const void* dataPtr) { colPtr_p->putColumnSliceCells (rownrs.convert(refTabPtr_p->rowNumbers()), ns, dataPtr); } ColumnCache& RefColumn::columnCache() { return colCache_p; } void RefColumn::setMaximumCacheSize (uInt nbytes) { colPtr_p->setMaximumCacheSize (nbytes); } void RefColumn::makeSortKey (Sort& sortobj, CountedPtr& cmpObj, Int order, const void*& dataSave) { colPtr_p->makeRefSortKey (sortobj, cmpObj, order, refTabPtr_p->rowNumbers(), dataSave); } void RefColumn::freeSortKey (const void*& dataSave) { colPtr_p->freeSortKey (dataSave); } void RefColumn::allocIterBuf (void*& lastVal, void*& curVal, CountedPtr& cmpObj) { colPtr_p->allocIterBuf (lastVal, curVal, cmpObj); } void RefColumn::freeIterBuf (void*& lastVal, void*& curVal) { colPtr_p->freeIterBuf (lastVal, curVal); } } //# NAMESPACE CASACORE - END casacore-2.4.1/tables/Tables/RefColumn.h000066400000000000000000000244151321422335000200260ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef TABLES_REFCOLUMN_H #define TABLES_REFCOLUMN_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class RefTable; class BaseColumnDesc; class TableRecord; class Slicer; class IPosition; template class Vector; // // 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 uInt nrow() const; // Test if a value in a particular cell has been defined. virtual Bool isDefined (uInt rownr) const; // Set the shape of the array in the given row. virtual void setShape (uInt rownr, const IPosition& shape); // Set the shape and tile shape of the array in the given row. virtual void setShape (uInt 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 (uInt rownr) const; // Get the shape of an array in a particular cell. virtual IPosition shape (uInt rownr) const; // It can change shape if the underlying column can. virtual Bool canChangeShape() const; // It can handle a scalar column if the underlying column // can handle cells in a scalar column. virtual Bool canAccessScalarColumn (Bool& reask) const; // It can handle an array column if the underlying column // can handle cells in an array column. virtual Bool canAccessArrayColumn (Bool& reask) const; // It can handle a cell slice if the underlying column can do it. virtual Bool canAccessSlice (Bool& reask) const; // It can handle a column slice if the underlying column // can handle a collection of cells in a column and a column slice. virtual Bool canAccessColumnSlice (Bool& reask) const; // It can handle cells in a scalar column if the underlying column // can do it. virtual Bool canAccessScalarColumnCells (Bool& reask) const; // It can handle cells in an array column if the underlying column // can do it. virtual Bool canAccessArrayColumnCells (Bool& reask) const; // Initialize the rows from startRownr till endRownr (inclusive) // with the default value defined in the column description (if defined). void initialize (uInt startRownr, uInt endRownr); // Get the value from a particular cell. // This can be a scalar or an array. virtual void get (uInt rownr, void* dataPtr) const; // Get a slice of an N-dimensional array in a particular cell. virtual void getSlice (uInt rownr, const Slicer&, void* dataPtr) const; // Get the vector of all scalar values in a column. virtual void getScalarColumn (void* 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 (void* 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&, void* dataPtr) const; // Get the vector of some scalar values in a column. virtual void getScalarColumnCells (const RefRows& rownrs, void* 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, void* 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&, void* dataPtr) const; // Put the value in a particular cell. // This can be a scalar or an array. virtual void put (uInt rownr, const void* dataPtr); // Put a slice of an N-dimensional array in a particular cell. virtual void putSlice (uInt rownr, const Slicer&, const void* dataPtr); // Put the vector of all scalar values in the column. virtual void putScalarColumn (const void* 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 void* 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 void* dataPtr); // Get the vector of some scalar values in a column. virtual void putScalarColumnCells (const RefRows& rownrs, const void* 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 void* 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 void* 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&, CountedPtr& cmpObj, Int order, const void*& dataSave); // Free storage on the heap allocated by makeSortkey(). // The pointer will be set to zero. virtual void freeSortKey (const void*& 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, CountedPtr& 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-2.4.1/tables/Tables/RefRows.cc000066400000000000000000000137561321422335000176670ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN RefRows::RefRows (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); uInt start = 0; uInt end = 0; uInt incr = 0; uInt nv = 0; uInt nr = 0; for (uInt 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 (uInt start, uInt end, uInt 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)); } uInt RefRows::fillNrows() const { uInt nr = 0; uInt n = itsRows.nelements(); for (uInt i=0; iitsNrows = nr; return nr; } Vector RefRows::convert (const Vector& rootRownrs) const { uInt n = nrow(); Vector rownrs(n); if (itsSliced) { uInt nr = 0; RefRowsSliceIter iter(*this); while (! iter.pastEnd()) { uInt rownr = iter.sliceStart(); uInt end = iter.sliceEnd(); uInt incr = iter.sliceIncr(); while (rownr <= end) { DebugAssert (rownr <= rootRownrs.nelements(), AipsError); rownrs(nr++) = rootRownrs(rownr); rownr += incr; } iter++; } } else { for (uInt i=0; i RefRows::convert() const { if (!itsSliced) { return itsRows; } uInt n = nrow(); Vector rownrs(n); uInt nr = 0; RefRowsSliceIter iter(*this); while (! iter.pastEnd()) { uInt rownr = iter.sliceStart(); uInt end = iter.sliceEnd(); uInt 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-2.4.1/tables/Tables/RefRows.h000066400000000000000000000174651321422335000175320ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 // 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); // Create the object from a single start,end,incr slice. RefRows (uInt start, uInt end, uInt 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 Vector 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. Vector convert (const Vector& rootRownrs) const; // Convert this object to a Vector by de-slicing it. // I.e. it linearizes the row numbers. Vector 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. // uInt nrows() const { return (itsNrows == 0 ? fillNrows() : itsNrows); } uInt nrow() const { return (itsNrows == 0 ? fillNrows() : itsNrows); } // // Return the first row in the object. uInt 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: // Fill the itsNrows variable. uInt fillNrows() const; Vector itsRows; uInt 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. // uInt rownr = rowiter.sliceStart(); // uInt end = rowiter.sliceEnd(); // uInt 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. // uInt sliceStart() const { return itsStart; } uInt sliceEnd() const { return itsEnd; } uInt sliceIncr() const { return itsIncr; } // private: Vector itsRows; Bool itsSliced; uInt itsStart; uInt itsEnd; uInt itsIncr; uInt itsPos; Bool itsPastEnd; }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/tables/Tables/RefTable.cc000066400000000000000000000741501321422335000177570ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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, uInt nrrow, int opt, const TableLock& lockOptions, const TSMOption& tsmOption) : BaseTable (name, opt, nrrow), rowStorage_p (0), // initially empty vector of rownrs nameMap_p (""), colMap_p (static_cast(0)), 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, uInt nrall) : BaseTable ("", Table::Scratch, nrall), baseTabPtr_p (btp->root()), rowOrd_p (order), rowStorage_p (nrall), // allocate vector of rownrs nameMap_p (""), colMap_p (static_cast(0)), changed_p (True) { rows_p = getStorage (rowStorage_p); //# Copy the table description and create the columns. tdescPtr_p = new TableDesc (btp->tableDesc(), TableDesc::Scratch); setup (btp, Vector()); //# Get root table (will be parent if btp is an reference table). //# Link to root table (ie. increase its reference count). baseTabPtr_p->link(); TableTrace::traceRefTable (baseTabPtr_p->tableName(), 's'); } RefTable::RefTable (BaseTable* btp, const Vector& rownrs) : BaseTable ("", Table::Scratch, rownrs.nelements()), baseTabPtr_p (btp->root()), rowOrd_p (True), rowStorage_p (0), nameMap_p (""), colMap_p (static_cast(0)), changed_p (True) { //# Copy the table description and create the columns. tdescPtr_p = new TableDesc (btp->tableDesc(), TableDesc::Scratch); setup (btp, Vector()); rowStorage_p = rownrs; rows_p = getStorage (rowStorage_p); //# Check if the row numbers do not exceed #rows. uInt nmax = btp->nrow(); for (uInt i=0; i= nmax) { throw (indexError ((Int)rows_p[i], "RefTable Row vector")); } } //# Adjust rownrs in case input table is a reference table. //# Link to the root table. rowOrd_p = btp->adjustRownrs (nrrow_p, rowStorage_p, True); baseTabPtr_p->link(); TableTrace::traceRefTable (baseTabPtr_p->tableName(), 's'); } RefTable::RefTable (BaseTable* btp, const Vector& mask) : BaseTable ("", Table::Scratch, 0), baseTabPtr_p (btp->root()), rowOrd_p (btp->rowOrder()), rowStorage_p (0), // initially empty vector of rownrs nameMap_p (""), colMap_p (static_cast(0)), changed_p (True) { //# Copy the table description and create the columns. tdescPtr_p = new TableDesc (btp->tableDesc(), TableDesc::Scratch); setup (btp, Vector()); //# Store the rownr if the mask is set. uInt nr = min (mask.nelements(), btp->nrow()); for (uInt i=0; iadjustRownrs (nrrow_p, rowStorage_p, True); baseTabPtr_p->link(); TableTrace::traceRefTable (baseTabPtr_p->tableName(), 's'); } RefTable::RefTable (BaseTable* btp, const Vector& columnNames) : BaseTable ("", Table::Scratch, btp->nrow()), baseTabPtr_p (btp->root()), rowOrd_p (btp->rowOrder()), rowStorage_p (0), nameMap_p (""), colMap_p (static_cast(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 = new TableDesc (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(); rows_p = getStorage (rowStorage_p); //# Link to the root table. baseTabPtr_p->link(); 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 (uInt i=0; i& 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(); } } uInt* RefTable::getStorage (Vector& rownrs) { Bool deleteIt; uInt* p = rownrs.getStorage (deleteIt); AlwaysAssert (deleteIt == False, AipsError); return p; } 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 (uInt nr, Vector& rowStorage, Bool determineOrder) const { uInt* rownrs = getStorage (rowStorage); Bool rowOrder = True; for (uInt i=0; itableName(), 'w'); AipsIO ios; writeStart (ios, True); ios << "RefTable"; ios.putstart ("RefTable", 2); // 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; ios << baseTabPtr_p->nrow(); ios << rowOrd_p; ios << nrrow_p; // Do not write more than 2**20 rownrs at once (CAS-7020). uInt done = 0; while (done < nrrow_p) { uInt todo = std::min(nrrow_p-done, 1048576u); ios.put (todo, rows_p+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; uInt rootNrow, nrrow; Int version = ios.getstart ("RefTable"); ios >> rootName; rootName = Path::addDirectory (rootName, tableName()); ios >> nameMap_p; Vector names; if (version > 1) { ios >> names; } ios >> rootNrow; ios >> rowOrd_p; ios >> nrrow; DebugAssert (nrrow == nrrow_p, AipsError); //# Resize the block of rownrs and read them in. rowStorage_p.resize (nrrow); rows_p = getStorage (rowStorage_p); // Do not read more than 2**20 rows at once (CAS-7020). uInt done = 0; while (done < nrrow) { uInt todo = std::min(nrrow_p-done, 1048576u); ios.get (todo, rows_p+done); done += todo; } 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(); 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 = new TableDesc (rootDesc, "", "", TableDesc::Scratch, False); makeDesc (*tdescPtr_p, rootDesc, nameMap_p, names); //# Create the refColumns. makeRefCol(); //# Read the TableInfo object. getTableInfo(); //# Great, everything is done. //# Now link to the root table. baseTabPtr_p->link(); } //# Read description and #rows. void RefTable::getLayout (TableDesc& desc, AipsIO& ios) { String rootName; SimpleOrderedMap 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; Table::getLayout (pdesc, rootName); makeDesc (desc, pdesc, nameMap, names); } void RefTable::makeDesc (TableDesc& desc, const TableDesc& rootDesc, SimpleOrderedMap& 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. uInt i; if (names.nelements() == 0) { names.resize (nameMap.ndefined()); for (i=0; i unknownCol (static_cast(0)); for (i=0; i(0)); } } //# Remove the unknown ones. for (i=0; i& 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 SimpleOrderedMap& nm = rtp->nameMap_p; for (uInt i=0; incolumn(); i++) { nameMap_p.define (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.define (cd.name(), cd.makeRefColumn (this, baseTabPtr_p->getColumn(nameMap_p(cd.name())))); } } //# Add column to this object for an addColumn. void RefTable::addRefCol (const ColumnDesc& columnDesc) { ColumnDesc& cd = tdescPtr_p->addColumn (columnDesc); nameMap_p.define (cd.name(), cd.name()); // Use cd (and not columnDesc) because underneath a pointer to its // BaseColumnDesc which is disastrous for the temporary columnDesc. colMap_p.define (cd.name(), cd.makeRefColumn (this, baseTabPtr_p->getColumn(nameMap_p(cd.name())))); changed_p = True; } void RefTable::addRefCol (const TableDesc& tdesc) { for (uInt i=0; i= nrow) { nrow = max ( nrow + 1024, uInt(1.2f * nrow)); rowStorage_p.resize (nrow, True); rows_p = getStorage (rowStorage_p); } rows_p[nrrow_p++] = rnr; changed_p = True; } //# Set exact number of rows. void RefTable::setNrrow (uInt nrrow) { if (nrrow > nrrow_p) { throw (TableError ("RefTable::setNrrow: exceeds current nrrow")); } rows_p = getStorage (rowStorage_p); 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 map("", nameMap_p.ndefined()); for (uInt i=0; idataManagerInfo(); // Invert the map to get map of old to new name. SimpleOrderedMap map("", nameMap_p.ndefined()); for (uInt i=0; i0;) { i--; Record& rec = dmi.rwSubRecord(i); Vector vec (rec.asArrayString ("COLUMNS")); Vector newVec(vec.nelements()); uInt nc=0; for (uInt j=0; jtableName() << " (" << 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(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(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 { uInt nrow = rownrs.nelements(); Vector rnr(nrow); for (uInt 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 (uInt rownr) { if (rownr >= nrrow_p) { throw (TableInvOper ("removeRow: rownr out of bounds")); } if (rownr < nrrow_p - 1) { objmove (rows_p+rownr, rows_p+rownr+1, nrrow_p-rownr-1); } nrrow_p--; changed_p = True; } void RefTable::removeColumn (const Vector& columnNames) { checkRemoveColumn (columnNames, True); for (uInt i=0; iremoveColumn (name); nameMap_p.remove (name); delete colMap_p(name); colMap_p.remove (name); } changed_p = True; } void RefTable::renameColumn (const String& newName, const String& oldName) { tdescPtr_p->renameColumn (newName, oldName); colMap_p.rename (newName, oldName); nameMap_p.rename (newName, oldName); 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(name); } return baseTabPtr_p->findDataManager (origName, byColumn); } // And 2 index arrays, which are both in ascending order. void RefTable::refAnd (uInt nr1, const uInt* inx1, uInt nr2, const uInt* inx2) { uInt allrow = (nr1 < nr2 ? nr1 : nr2); // max #output rows rowStorage_p.resize (allrow); // allocate output storage rows_p = getStorage (rowStorage_p); uInt i1, i2, row1, row2; i1 = i2 = 0; while (True) { if (i1 >= nr1) { row1 = 0xffffffff; // end of inx1 }else{ row1 = inx1[i1]; // next element in inx1 } if (i2 >= nr2) { row2 = 0xffffffff; // end of inx2 }else{ row2 = inx2[i2]; // next element in inx2 } if (row1 == row2) { if (row1 == 0xffffffff) break; // end of both inx rows_p[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 (uInt nr1, const uInt* inx1, uInt nr2, const uInt* inx2) { uInt allrow = nr1 + nr2; // max #output rows rowStorage_p.resize (allrow); // allocate output storage rows_p = getStorage (rowStorage_p); uInt i1, i2, row1, row2; i1 = i2 = 0; while (True) { if (i1 >= nr1) { row1 = 0xffffffff; // end of inx1 }else{ row1 = inx1[i1]; // next element in inx1 } if (i2 >= nr2) { row2 = 0xffffffff; // end of inx2 }else{ row2 = inx2[i2]; // next element in inx2 } if (row1 == row2) { if (row1 == 0xffffffff) break; // end of both inx rows_p[nrrow_p++] = row1; i1++; i2++; }else{ if (row1 < row2) { rows_p[nrrow_p++] = row1; i1++; // next inx1 }else{ rows_p[nrrow_p++] = row2; i2++; // next inx2 } } } changed_p = True; } // Subtract 2 index arrays, which are both in ascending order. void RefTable::refSub (uInt nr1, const uInt* inx1, uInt nr2, const uInt* inx2) { uInt allrow = nr1; // max #output rows rowStorage_p.resize (allrow); // allocate output storage rows_p = getStorage (rowStorage_p); uInt i1, i2, row1, row2; i1 = i2 = 0; while (True) { if (i1 >= nr1) { row1 = 0xffffffff; // end of inx1 }else{ row1 = inx1[i1]; // next element in inx1 } if (i2 >= nr2) { row2 = 0xffffffff; // end of inx2 }else{ row2 = inx2[i2]; // next element in inx2 } if (row1 == row2) { if (row1 == 0xffffffff) break; // end of both inx i1++; i2++; }else{ if (row1 < row2) { rows_p[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 (uInt nr1, const uInt* inx1, uInt nr2, const uInt* inx2) { uInt allrow = nr1 + nr2; // max #output rows rowStorage_p.resize (allrow); // allocate output storage rows_p = getStorage (rowStorage_p); uInt i1, i2, row1, row2; i1 = i2 = 0; while (True) { if (i1 >= nr1) { row1 = 0xffffffff; // end of inx1 }else{ row1 = inx1[i1]; // next element in inx1 } if (i2 >= nr2) { row2 = 0xffffffff; // end of inx2 }else{ row2 = inx2[i2]; // next element in inx2 } if (row1 == row2) { if (row1 == 0xffffffff) break; // end of both inx i1++; i2++; }else{ if (row1 < row2) { rows_p[nrrow_p++] = row1; i1++; // next inx1 }else{ rows_p[nrrow_p++] = row2; i2++; // next inx2 } } } changed_p = True; } // Negate a table. void RefTable::refNot (uInt nr, const uInt* inx, uInt 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. uInt allrow = nrtot - nr; // #output rows rowStorage_p.resize (allrow); // allocate output storage rows_p = getStorage (rowStorage_p); uInt start = 0; uInt 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, uInt 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, uInt 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(); // 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 (uInt rownr); // 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. uInt rootRownr (uInt 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 (uInt rownr); // Set the exact number of rows in the table. // An exception is thrown if more than current nrrow. void setNrrow (uInt 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 (uInt nrrow, Vector& rownrs, Bool determineOrder) const; // And, or, subtract or xor the row numbers of 2 tables. void refAnd (uInt nr1, const uInt* rows1, uInt nr2, const uInt* rows2); void refOr (uInt nr1, const uInt* rows1, uInt nr2, const uInt* rows2); void refSub (uInt nr1, const uInt* rows1, uInt nr2, const uInt* rows2); void refXor (uInt nr1, const uInt* rows1, uInt nr2, const uInt* rows2); void refNot (uInt nr1, const uInt* rows1, uInt nrmain); // Get the internal pointer in a rowStorage vector. // It checks whether no copy is made of the data. static uInt* getStorage (Vector& rownrs); private: BaseTable* baseTabPtr_p; //# pointer to parent table Bool rowOrd_p; //# True = table is in row order Vector rowStorage_p; //# row numbers in parent table uInt* rows_p; //# Pointer to rowStorage_p SimpleOrderedMap nameMap_p; //# map to column name in parent SimpleOrderedMap colMap_p; //# map name to column Bool changed_p; //# True = changed since last write // Copy constructor is forbidden, because copying a table requires // some more knowledge (like table name of result). // Declaring it private, makes it unusable. RefTable (const RefTable&); // Assignment is forbidden, because copying a table requires // some more knowledge (like table name of result). // Declaring it private, makes it unusable. RefTable& operator= (const RefTable&); // 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, SimpleOrderedMap& 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 uInt RefTable::rootRownr (uInt rnr) const { return rows_p[rnr]; } } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/tables/Tables/RowCopier.cc000066400000000000000000000122741321422335000202030ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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(); void attach(const String &outCol, const String &inCol); Bool copy(uInt toRow, uInt fromRow); private: //# The following constructors and operator don't seem to be useful ColumnHolder(); ColumnHolder(const ColumnHolder &other); ColumnHolder &operator=(const ColumnHolder &other); // 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(uInt toRow, uInt fromRow) { uInt i; if (fromRow >= in.nrow() || toRow >= out.nrow()) { return False; } // loop over all columns for (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 = new ColumnHolder(out,in); for (uInt i=0; i < out.tableDesc().ncolumn(); i++) { TableColumn outCol(out, i); if (in.tableDesc().isColumn(outCol.columnDesc().name())) { TableColumn inCol(in, outCol.columnDesc().name()); columns_p->attach(outCol.columnDesc().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 = new ColumnHolder(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(uInt toRow, uInt fromRow) { return columns_p->copy(toRow, fromRow); } RowCopier::~RowCopier() {} } //# NAMESPACE CASACORE - END casacore-2.4.1/tables/Tables/RowCopier.h000066400000000000000000000172411321422335000200440ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef TABLES_ROWCOPIER_H #define TABLES_ROWCOPIER_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class Table; template class Vector; 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 (uInt 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); // The things that actually do the copying when requested. // // Copy different row numbers. Bool copy (uInt toRow, uInt fromRow); // Copy to and from the same row number Bool copy (uInt rownr); // ~RowCopier(); private: //# The following constructors and operator don't seem to be useful // RowCopier(); RowCopier(const RowCopier &other); RowCopier &operator=(const RowCopier &other); // // The ColumnHolder class exists only in the .cc file, it is what // ultimately does the work. CountedPtr columns_p; }; inline Bool RowCopier::copy (uInt rownr) { return copy (rownr, rownr); } } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/tables/Tables/ScaColData.h000066400000000000000000000175201321422335000200710ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef TABLES_SCACOLDATA_H #define TABLES_SCACOLDATA_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class ColumnSet; template class ScalarColumnDesc; class AipsIO; template class Vector; // // 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(); // Ask if the data manager can handle a column. Bool canAccessScalarColumn (Bool& reask) const; // Ask if the data manager can handle some cells in a column. Bool canAccessScalarColumnCells (Bool& reask) const; // Initialize the rows from startRownr till endRownr (inclusive) // with the default value defined in the column description. void initialize (uInt startRownr, uInt endRownr); // Test if the given cell contains a defined value. Bool isDefined (uInt rownr) const; // Get the value from a particular cell. void get (uInt 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. void getScalarColumn (void* 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. void getScalarColumnCells (const RefRows& rownrs, void* 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. void put (uInt 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. void putScalarColumn (const void* 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. void putScalarColumnCells (const RefRows& rownrs, const void* dataPtr); // 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. // //
      • TableInvSort // // void makeSortKey (Sort&, CountedPtr& cmpFunc, Int order, const void*& dataSave); // Do it only for the given row numbers. void makeRefSortKey (Sort&, CountedPtr& cmpFunc, Int order, const Vector& rownrs, const void*& dataSave); // // Free storage on the heap allocated by makeSortkey(). // The pointer will be set to zero. void freeSortKey (const void*& 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, CountedPtr& 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&, CountedPtr& cmpObj, Int order); }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-2.4.1/tables/Tables/ScaColData.tcc000066400000000000000000000223331321422335000204110ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 Bool ScalarColumnData::canAccessScalarColumn (Bool& reask) const { return dataColPtr_p->canAccessScalarColumn (reask); } template Bool ScalarColumnData::canAccessScalarColumnCells (Bool& reask) const { return dataColPtr_p->canAccessScalarColumnCells (reask); } template void ScalarColumnData::initialize (uInt startRow, uInt endRow) { if (colDescPtr_p->dataType() != TpOther) { for (uInt i=startRow; i<=endRow; i++) { dataColPtr_p->put (i, &(scaDescPtr_p->defaultValue())); } } } template Bool ScalarColumnData::isDefined (uInt rownr) const { if (!undefFlag_p) { return True; } T val; dataColPtr_p->get (rownr, &val); return ( (!(val == undefVal_p))); } template void ScalarColumnData::get (uInt rownr, void* val) const { if (rtraceColumn_p) { TableTrace::trace (traceId(), columnDesc().name(), 'r', rownr); } checkReadLock (True); dataColPtr_p->get (rownr, (T*)val); autoReleaseLock(); } template void ScalarColumnData::getScalarColumn (void* val) const { if (rtraceColumn_p) { TableTrace::trace (traceId(), columnDesc().name(), 'r'); } Vector* vecPtr = (Vector*)val; if (vecPtr->nelements() != nrow()) { throw (TableArrayConformanceError("ScalarColumnData::getScalarColumn")); } checkReadLock (True); dataColPtr_p->getScalarColumnV (vecPtr); autoReleaseLock(); } template void ScalarColumnData::getScalarColumnCells (const RefRows& rownrs, void* val) const { if (rtraceColumn_p) { TableTrace::trace (traceId(), columnDesc().name(), 'r', rownrs); } Vector& vec = *(Vector*)val; uInt nr = rownrs.nrow(); if (vec.nelements() != nr) { throw (TableArrayConformanceError("ScalarColumnData::getColumnCells")); } checkReadLock (True); dataColPtr_p->getScalarColumnCellsV (rownrs, &vec); autoReleaseLock(); } template void ScalarColumnData::put (uInt rownr, const void* val) { if (wtraceColumn_p) { TableTrace::trace (traceId(), columnDesc().name(), 'w', rownr); } checkValueLength ((const T*)val); checkWriteLock (True); dataColPtr_p->put (rownr, (const T*)val); autoReleaseLock(); } template void ScalarColumnData::putScalarColumn (const void* val) { if (wtraceColumn_p) { TableTrace::trace (traceId(), columnDesc().name(), 'w'); } const Vector* vecPtr = (const Vector*)val; if (vecPtr->nelements() != nrow()) { throw (TableArrayConformanceError("ScalarColumnData::putColumn")); } checkValueLength (vecPtr); checkWriteLock (True); dataColPtr_p->putScalarColumnV (vecPtr); autoReleaseLock(); } template void ScalarColumnData::putScalarColumnCells (const RefRows& rownrs, const void* val) { if (wtraceColumn_p) { TableTrace::trace (traceId(), columnDesc().name(), 'w', rownrs); } const Vector& vec = *(const Vector*)val; if (vec.nelements() != rownrs.nrow()) { throw (TableArrayConformanceError("ScalarColumnData::putColumn")); } checkValueLength (&vec); checkWriteLock (True); dataColPtr_p->putScalarColumnCellsV (rownrs, &vec); autoReleaseLock(); } template void ScalarColumnData::makeSortKey (Sort& sortobj, CountedPtr& cmpObj, Int order, const void*& dataSave) { //#// Optimal is to ask the data manager for a pointer to //#// the consecutive data. Often this may succeed. //# Get the data as a column. //# Save the pointer to the vector for deletion by freeSortKey(). dataSave = 0; uInt nrrow = nrow(); Vector* vecPtr = new Vector(nrrow); Bool reask; if (canAccessScalarColumn (reask)) { getScalarColumn (vecPtr); }else{ checkReadLock (True); for (uInt i=0; iget (i, &(*vecPtr)(i)); } autoReleaseLock(); } dataSave = vecPtr; fillSortKey (vecPtr, sortobj, cmpObj, order); } template void ScalarColumnData::makeRefSortKey (Sort& sortobj, CountedPtr& cmpObj, Int order, const Vector& rownrs, const void*& dataSave) { //#// Optimal is to ask the data manager for a pointer to //#// the consecutive data. Often this may succeed. //# Get the data as a column. dataSave = 0; uInt nrrow = rownrs.nelements(); Vector* vecPtr = new Vector(nrrow); Bool reask; if (canAccessScalarColumnCells (reask)) { getScalarColumnCells (rownrs, vecPtr); }else{ checkReadLock (True); for (uInt i=0; iget (rownrs(i), &(*vecPtr)(i)); } autoReleaseLock(); } dataSave = vecPtr; fillSortKey (vecPtr, sortobj, cmpObj, order); } template void ScalarColumnData::fillSortKey (const Vector* vecPtr, Sort& sortobj, CountedPtr& 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. Bool deleteIt; const T* datap = vecPtr->getStorage (deleteIt); if (cmpObj.null()) { cmpObj = new ObjCompare(); } sortobj.sortKey (datap, cmpObj, sizeof(T), order == Sort::Descending ? Sort::Descending : Sort::Ascending); vecPtr->freeStorage (datap, deleteIt); } template void ScalarColumnData::freeSortKey (const void*& dataSave) { if (dataSave != 0) { delete (Vector*)dataSave; } dataSave = 0; } template void ScalarColumnData::allocIterBuf (void*& lastVal, void*& curVal, CountedPtr& cmpObj) { T* valp = new T[2]; lastVal = valp; curVal = valp + 1; if (cmpObj.null()) { cmpObj = new ObjCompare; } } 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-2.4.1/tables/Tables/ScaColDesc.h000066400000000000000000000240711321422335000200750ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef TABLES_SCACOLDESC_H #define TABLES_SCACOLDESC_H //# Includes #include #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, 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 #ifdef AIPS_CXX11 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; #endif } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-2.4.1/tables/Tables/ScaColDesc.tcc000066400000000000000000000130641321422335000204170ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/tables/Tables/ScaColDesc_tmpl.cc000066400000000000000000000036201321422335000212640ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: Vector.h 21545 2015-01-22 19:36:35Z gervandiepen $ //# Includes #include //# Instantiate extern templates for often used types. #ifdef AIPS_CXX11 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; } #endif casacore-2.4.1/tables/Tables/ScaRecordColData.cc000066400000000000000000000171071321422335000213670ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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), scaDescPtr_p (cd) {} 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, ""); } Bool ScalarRecordColumnData::canAccessScalarColumn (Bool& reask) const { reask = False; return True; } Bool ScalarRecordColumnData::canAccessScalarColumnCells (Bool& reask) const { reask = False; return True; } void ScalarRecordColumnData::initialize (uInt, uInt) {} Bool ScalarRecordColumnData::isDefined (uInt) const { return True; } void ScalarRecordColumnData::get (uInt rownr, void* val) const { checkReadLock (True); getRecord (rownr, *(TableRecord*)val); autoReleaseLock(); } void ScalarRecordColumnData::getScalarColumn (void* val) const { Vector& vec = *(Vector*)val; uInt nr = nrow(); if (vec.nelements() != nr) { throw (TableArrayConformanceError ("ScalarRecordColumnData::getScalarColumn")); } checkReadLock (True); for (uInt i=0; i& vec = *(Vector*)val; if (vec.nelements() != rownrs.nrow()) { throw (TableArrayConformanceError ("ScalarRecordColumnData::getColumnCells")); } checkReadLock (True); RefRowsSliceIter iter(rownrs); uInt i=0; while (! iter.pastEnd()) { uInt rownr = iter.sliceStart(); uInt end = iter.sliceEnd(); uInt incr = iter.sliceIncr(); while (rownr <= end) { getRecord (rownr, vec(i++)); rownr += incr; } iter++; } autoReleaseLock(); } void ScalarRecordColumnData::put (uInt rownr, const void* val) { checkWriteLock (True); putRecord (rownr, *(const TableRecord*)val); autoReleaseLock(); } void ScalarRecordColumnData::putScalarColumn (const void* val) { const Vector& vec = *(const Vector*)val; uInt nr = nrow(); if (vec.nelements() != nr) { throw (TableArrayConformanceError ("ScalarRecordColumnData::putScalarColumn")); } checkWriteLock (True); for (uInt i=0; i& vec = *(const Vector*)val; if (vec.nelements() != rownrs.nrow()) { throw (TableArrayConformanceError ("ScalarRecordColumnData::putColumnCells")); } checkWriteLock (True); RefRowsSliceIter iter(rownrs); uInt i=0; while (! iter.pastEnd()) { uInt rownr = iter.sliceStart(); uInt end = iter.sliceEnd(); uInt incr = iter.sliceIncr(); while (rownr <= end) { putRecord (rownr, vec(i++)); rownr += incr; } iter++; } autoReleaseLock(); } void ScalarRecordColumnData::getRecord (uInt 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); MemoryIO memio (buf, shape(0)); AipsIO aio(&memio); rec.getRecord (aio, TableAttr(dataManager()->table())); data.freeStorage (buf, deleteIt); } } void ScalarRecordColumnData::putRecord (uInt rownr, const TableRecord& rec) { MemoryIO memio; 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&, CountedPtr&, Int, const void*&) { throw (TableError ("Sorting on a column containing records " "is not possible")); } void ScalarRecordColumnData::makeRefSortKey (Sort&, CountedPtr&, Int, const Vector&, const void*&) { throw (TableError ("Sorting on a column containing records " "is not possible")); } void ScalarRecordColumnData::freeSortKey (const void*& dataSave) { dataSave = 0; } void ScalarRecordColumnData::allocIterBuf (void*&, void*&, CountedPtr&) { 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-2.4.1/tables/Tables/ScaRecordColData.h000066400000000000000000000174301321422335000212300ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef TABLES_SCARECORDCOLDATA_H #define TABLES_SCARECORDCOLDATA_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class ColumnSet; class ScalarRecordColumnDesc; class AipsIO; template class Vector; // // 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(); // Ask if the data manager can handle a column. virtual Bool canAccessScalarColumn (Bool& reask) const; // Ask if the data manager can handle some cells in a column. virtual Bool canAccessScalarColumnCells (Bool& reask) const; // Initialize the rows from startRownr till endRownr (inclusive) // with the default value defined in the column description. virtual void initialize (uInt startRownr, uInt endRownr); // Test if the given cell contains a defined value. virtual Bool isDefined (uInt rownr) const; // Get the value from a particular cell. virtual void get (uInt 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 (void* 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, void* 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 (uInt 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 void* 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 void* 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&, CountedPtr& cmpObj, Int order, const void*& dataSave); // Do it only for the given row numbers. virtual void makeRefSortKey (Sort&, CountedPtr& cmpObj, Int order, const Vector& rownrs, const void*& dataSave); // // Free storage on the heap allocated by makeSortkey(). // The pointer will be set to zero. virtual void freeSortKey (const void*& 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, CountedPtr& 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: // Pointer to column description. const ScalarRecordColumnDesc* scaDescPtr_p; // Copy constructor cannot be used. ScalarRecordColumnData (const ScalarRecordColumnData&); // Assignment cannot be used. ScalarRecordColumnData& operator= (const ScalarRecordColumnData&); // 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 (uInt rownr, TableRecord& rec) const; void putRecord (uInt rownr, const TableRecord& rec); // }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/tables/Tables/ScaRecordColDesc.cc000066400000000000000000000075641321422335000214020ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/tables/Tables/ScaRecordColDesc.h000066400000000000000000000142561321422335000212400ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef TABLES_SCARECORDCOLDESC_H #define TABLES_SCARECORDCOLDESC_H //# Includes #include #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-2.4.1/tables/Tables/ScalarColumn.h000066400000000000000000000252611321422335000205170ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef TABLES_SCALARCOLUMN_H #define TABLES_SCALARCOLUMN_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class BaseColumn; class RefRows; template class Vector; 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 (uInt 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 (uInt rownr) const { T value; get (rownr, value); return value; } T operator() (uInt 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 (uInt 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 (uInt 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 (uInt thisRownr, const ScalarColumn& that, uInt 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 (uInt 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 (uInt thisRownr, const TableColumn& that, uInt 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; protected: // Keep a switch to determine if an entire column can be accessed. // True = yes; False = no. mutable Bool canAccessColumn_p; // Keep a switch to know if access knowledge is permanent or has // to be asked again the next time. mutable Bool reaskAccessColumn_p; }; //# Explicitly instantiate these templates in ScalarColumn_tmpl.cc #ifdef AIPS_CXX11 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; #endif } //# 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-2.4.1/tables/Tables/ScalarColumn.tcc000066400000000000000000000211061321422335000210330ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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(), canAccessColumn_p (False), reaskAccessColumn_p (True) {} template ScalarColumn::ScalarColumn (const Table& tab, const String& columnName) : TableColumn (tab, columnName), canAccessColumn_p (False), reaskAccessColumn_p (True) { checkDataType(); } template ScalarColumn::ScalarColumn (const TableColumn& column) : TableColumn (column), canAccessColumn_p (False), reaskAccessColumn_p (True) { checkDataType(); } template ScalarColumn::ScalarColumn (const ScalarColumn& that) : TableColumn (that), canAccessColumn_p (that.canAccessColumn_p), reaskAccessColumn_p (that.reaskAccessColumn_p) {} 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); canAccessColumn_p = that.canAccessColumn_p; reaskAccessColumn_p = that.reaskAccessColumn_p; } } 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 { uInt 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")); } } //# Ask if we can access the column (if that is not known yet). if (reaskAccessColumn_p) { canAccessColumn_p = baseColPtr_p->canAccessScalarColumn (reaskAccessColumn_p); } //# Access the column if possible. //# Otherwise fill the entire vector by looping through all cells. if (canAccessColumn_p) { baseColPtr_p->getScalarColumn (&vec); }else{ for (uInt rownr=0; rownrget (rownr, &(vec(rownr))); } } } 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 { uInt 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. uInt 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 (uInt thisRownr, const ScalarColumn& that, uInt thatRownr) { put (thisRownr, that(thatRownr)); } template void ScalarColumn::put (uInt thisRownr, const TableColumn& that, uInt thatRownr, Bool) { T value; that.getScalarValue (thatRownr, &value, columnDesc().dataTypeId()); put (thisRownr, value); } template void ScalarColumn::putColumn (const Vector& vec) { checkWritable(); uInt nrrow = nrow(); //# Check the vector length. if (vec.nelements() != nrrow) { throw (TableConformanceError("ScalarColumn::putColumn(Vector&)")); } //# Ask if we can access the column (if that is not known yet). if (reaskAccessColumn_p) { canAccessColumn_p = baseColPtr_p->canAccessScalarColumn (reaskAccessColumn_p); } //# Access the column if possible. //# Otherwise put the entire vector by looping through all cells. if (canAccessColumn_p) { baseColPtr_p->putScalarColumn (&vec); }else{ for (uInt rownr=0; rownrput (rownr, &(vec(rownr))); } } } template void ScalarColumn::putColumnRange (const Slicer& rowRange, const Vector& vec) { uInt 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. uInt 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) { uInt nrrow = nrow(); for (uInt i=0; i void ScalarColumn::putColumn (const ScalarColumn& that) { //# Check the column lengths. uInt nrrow = nrow(); if (nrrow != that.nrow()) { throw (TableConformanceError ("ScalarColumn::putColumn")); } for (uInt i=0; i //# Instantiate extern templates for often used types. #ifdef AIPS_CXX11 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; } #endif casacore-2.4.1/tables/Tables/ScaledArrayEngine.h000066400000000000000000000003771321422335000214550ustar00rootroot00000000000000#ifndef TABLES_TABLES_STUB_SCALEDARRAYENGINE_H #define TABLES_TABLES_STUB_SCALEDARRAYENGINE_H #include #warning "Tables/ScaledArrayEngine.h has been deprecated; use DataMan/ScaledArrayEngine.h instead" #endif casacore-2.4.1/tables/Tables/ScaledComplexData.h000066400000000000000000000003771321422335000214520ustar00rootroot00000000000000#ifndef TABLES_TABLES_STUB_SCALEDCOMPLEXDATA_H #define TABLES_TABLES_STUB_SCALEDCOMPLEXDATA_H #include #warning "Tables/ScaledComplexData.h has been deprecated; use DataMan/ScaledComplexData.h instead" #endif casacore-2.4.1/tables/Tables/SetupNewTab.cc000066400000000000000000000262021321422335000204670ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 = new SetupNewTableRep (tableName, tableDescName, opt, storageOpt); } SetupNewTable::SetupNewTable (const String& tableName, const TableDesc& tableDesc, Table::TableOption opt, const StorageOption& storageOpt) { newTable_p = new SetupNewTableRep (tableName, tableDesc, opt, storageOpt); } SetupNewTable::SetupNewTable (const SetupNewTable& that) : newTable_p(0) { operator= (that); } SetupNewTable::~SetupNewTable() { if (newTable_p != 0) { if (--(newTable_p->count()) == 0) { delete newTable_p; } } } SetupNewTable& SetupNewTable::operator= (const SetupNewTable& that) { if (newTable_p != 0) { if (--(newTable_p->count()) == 0) { delete newTable_p; } } newTable_p = that.newTable_p; if (newTable_p != 0) { ++(newTable_p->count()); } return *this; } SetupNewTableRep::SetupNewTableRep (const String& tableName, const String& tableDescName, Table::TableOption opt, const StorageOption& storageOpt) : count_p (1), tabName_p (tableName), option_p (opt), storageOpt_p(storageOpt), delete_p (False), tdescPtr_p (0), colSetPtr_p (0), dataManMap_p(static_cast(0)) { //# Copy the table description. tdescPtr_p = new TableDesc(tableDescName); //# Setup the new table. setup(); } SetupNewTableRep::SetupNewTableRep (const String& tableName, const TableDesc& tableDesc, Table::TableOption opt, const StorageOption& storageOpt) : count_p (1), tabName_p (tableName), option_p (opt), storageOpt_p(storageOpt), delete_p (False), tdescPtr_p (0), colSetPtr_p (0), dataManMap_p(static_cast(0)) { //# Read the table description. tdescPtr_p = new TableDesc(tableDesc, "", "", TableDesc::Scratch); //# Setup the new table. setup(); } SetupNewTableRep::~SetupNewTableRep() { //# When the object is in use, the ColumnSet and table //# description pointers are taken over by the PlainTable object. //# So only delete them if not in use. if (! isUsed()) { delete tdescPtr_p; delete colSetPtr_p; } } 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 = new ColumnSet(tdescPtr_p, 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 = (DataManager*) (dataManMap_p((void*)&dataMan)); 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")); DataManager* 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-2.4.1/tables/Tables/SetupNewTab.h000066400000000000000000000432601321422335000203340ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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(); // Get access to the reference count. uInt& count() { return count_p; } // 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 == 0 ? True : False); } // Get pointer to column set. // This function is used by PlainTable. ColumnSet* columnSetPtr() { return colSetPtr_p; } // Get pointer to table description. // This function is used by PlainTable. TableDesc* tableDescPtr() { return tdescPtr_p; } // Set object to in use by a (Plain)Table object. // This function is used by PlainTable. void setInUse() { colSetPtr_p = 0; } // Make a data manager for all unbound columns. void handleUnbound(); private: // Reference count. uInt count_p; // Table name. String tabName_p; // Constructor options. int option_p; StorageOption storageOpt_p; // Marked for delete? Bool delete_p; TableDesc* tdescPtr_p; ColumnSet* colSetPtr_p; //# 0 = object is already used by a Table SimpleOrderedMap dataManMap_p; // Copy constructor is forbidden, because copying a table requires // some more knowledge (like table name of result). // Declaring it private, makes it unusable. SetupNewTableRep (const SetupNewTableRep&); // Assignment is forbidden, because copying a table requires // some more knowledge (like table name of result). // Declaring it private, makes it unusable. SetupNewTableRep& operator= (const SetupNewTableRep&); // 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 SimpleOrderedMap& 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. SetupNewTableRep* newTable_p; // Get pointer to column set. // This function is used by PlainTable. ColumnSet* columnSetPtr() { return newTable_p->columnSetPtr(); } // Get pointer to table description. // This function is used by PlainTable. TableDesc* tableDescPtr() { 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-2.4.1/tables/Tables/StManAipsIO.h000066400000000000000000000003411321422335000202130ustar00rootroot00000000000000#ifndef TABLES_TABLES_STUB_STMANAIPSIO_H #define TABLES_TABLES_STUB_STMANAIPSIO_H #include #warning "Tables/StManAipsIO.h has been deprecated; use DataMan/StManAipsIO.h instead" #endif casacore-2.4.1/tables/Tables/StandardStMan.h000066400000000000000000000003531321422335000206320ustar00rootroot00000000000000#ifndef TABLES_TABLES_STUB_STANDARDSTMAN_H #define TABLES_TABLES_STUB_STANDARDSTMAN_H #include #warning "Tables/StandardStMan.h has been deprecated; use DataMan/StandardStMan.h instead" #endif casacore-2.4.1/tables/Tables/StandardStManAccessor.h000066400000000000000000000004231321422335000223130ustar00rootroot00000000000000#ifndef TABLES_TABLES_STUB_STANDARDSTMANACCESSOR_H #define TABLES_TABLES_STUB_STANDARDSTMANACCESSOR_H #include #warning "Tables/StandardStManAccessor.h has been deprecated; use DataMan/StandardStManAccessor.h instead" #endif casacore-2.4.1/tables/Tables/StorageOption.cc000066400000000000000000000047641321422335000210740ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: StorageOption.cc 21154 2011-12-02 07:19:09Z gervandiepen $ #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN StorageOption::StorageOption (StorageOption::Option option, Int blockSize) : itsOption (option), itsBlockSize (blockSize) {} 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 is to use separate files. if (itsOption == StorageOption::Default) { itsOption = StorageOption::SepFile; } } } //# NAMESPACE CASACORE - END casacore-2.4.1/tables/Tables/StorageOption.h000066400000000000000000000100671321422335000207270ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: StorageOption.h 20859 2010-02-03 13:14:15Z gervandiepen $ #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. //
        // It is possible to specify the storage type and block size using aipsrc. // The aipsrc variables are: //
          //
        • tables.storage.type. The (case-insensitive) value can be // 'multifile' or 'multihdf5'. // Another value means the old way (separate files). //
        • tables.storage.blocksize gives the default blocksize to be // used for the multifile and multihdf5 option. //
        //
        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 MultiFile). 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. StorageOption (Option option=Aipsrc, Int blockSize=-2); // 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; } private: Option itsOption; Int itsBlockSize; }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/tables/Tables/SubTabDesc.cc000066400000000000000000000135711321422335000202520ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/tables/Tables/SubTabDesc.h000066400000000000000000000205241321422335000201100ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef TABLES_SUBTABDESC_H #define TABLES_SUBTABDESC_H //# Includes #include #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-2.4.1/tables/Tables/TSMOption.h000066400000000000000000000003271321422335000177640ustar00rootroot00000000000000#ifndef TABLES_TABLES_STUB_TSMOPTION_H #define TABLES_TABLES_STUB_TSMOPTION_H #include #warning "Tables/TSMOption.h has been deprecated; use DataMan/TSMOption.h instead" #endif casacore-2.4.1/tables/Tables/TVec.h000066400000000000000000000143271321422335000167760ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 uInt 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 (uInt index) const = 0; // Get a value. virtual void getVal (uInt index, T&) const = 0; // Put a value. virtual void putVal (uInt 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; Int nrel_p; //# #elements (<0 = ask derived class) // Get nr of elements. virtual uInt nelem() const; public: // Check if vectors are comformant. void validateConformance (uInt) 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 uInt 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-2.4.1/tables/Tables/TVec.tcc000066400000000000000000000044421321422335000173150ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 uInt TabVecRep::nelem() const { return nrel_p; } template void TabVecRep::validateConformance (uInt leng) const { if (nelements() != leng) { throw TableVectorNonConform(); } } //# Create a new vector (in memory). template void* TabVecRep::newVec() const { uInt nr = nelements(); TabVecTemp* tmvp = new TabVecTemp(nr); return tmvp; } template void TabVecRep::assign (const TabVecRep& that) { uInt nr = that.nelements(); validateConformance (nr); for (uInt 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-2.4.1/tables/Tables/TVecLogic.tcc000066400000000000000000000052121321422335000202670ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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) \ { \ uInt nr = r.nelements(); \ l.validateConformance(nr); \ Bool retval = True; \ for (uInt i=0; i \ Bool aips_name2(tabVecRepvalr,NAME) (const TabVecRep& tv, const T& val) \ { \ uInt nr = tv.nelements(); \ Bool retval = True; \ for (uInt i=0; i \ Bool aips_name2(tabVecRepvall,NAME) (const T& val, const TabVecRep& tv) \ { \ uInt nr = tv.nelements(); \ Bool retval = True; \ for (uInt i=0; i=) TVECLOGICOPER(GT,>) TVECLOGICOPER(EQ,==) TVECLOGICOPER(NE,!=) } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/tables/Tables/TVecMath.h000066400000000000000000000230671321422335000176110ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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&, Int start, Int 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-2.4.1/tables/Tables/TVecMath.tcc000066400000000000000000000167501321422335000201340ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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) \ { \ uInt nr = tv.nelements(); \ TabVecTemp& vec = *(TabVecTemp*)tv.newVec(); \ for (uInt i=0; i \ TabVecRep& aips_name2(tabVecRepvall,NAME) (const T& val, \ const TabVecRep& tv) \ { \ uInt nr = tv.nelements(); \ TabVecTemp& vec = *(TabVecTemp*)tv.newVec(); \ T tmp; \ for (uInt i=0; i \ TabVecRep& aips_name2(tabVecReptv,NAME) (const TabVecRep& tvl, \ const TabVecRep& tvr) \ { \ uInt nr = tvr.nelements(); \ tvl.validateConformance(nr); \ TabVecTemp& vec = *(TabVecTemp*)tvl.newVec(); \ for (uInt i=0; i \ void aips_name2(tabVecRepvalass,NAME) (TabVecRep& tv, const T& val) \ { \ uInt nr = tv.nelements(); \ T tmp; \ for (uInt i=0; i \ void aips_name2(tabVecReptvass,NAME) (TabVecRep& tvl, \ const TabVecRep& tvr) \ { \ uInt nr = tvr.nelements(); \ tvl.validateConformance(nr); \ T tmp; \ for (uInt i=0; i TabVecRep& tabVecRepnegate(const TabVecRep& tv) { uInt nr = tv.nelements(); TabVecTemp& vec = *(TabVecTemp*)tv.newVec(); T tmp; for (uInt i=0; i void tabVecRepminmax (T& min, T& max, const TabVecRep& tv) { uInt 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 (uInt i=1; i max) max = tmp; } } template void tabVecRepindgen(TabVecRep& tv, Int start, Int inc) { uInt nr = tv.nelements(); for (uInt i=0; i \ TabVecRep& aips_name2(tabVecRep,NAME) (const TabVecRep& tv) \ { \ uInt nr = tv.nelements(); \ TabVecTemp& vec = *(TabVecTemp*)tv.newVec(); \ T tmp; \ for (uInt i=0; i \ TabVecRep& aips_name2(tabVecRep,NAME) (const TabVecRep& tvl, \ const TabVecRep& tvr) \ { \ uInt nr = tvr.nelements(); \ tvl.validateConformance(nr); \ TabVecTemp& vec = *(TabVecTemp*)tvl.newVec(); \ T tmpl, tmpr; \ for (uInt i=0; i TabVecRep& tabVecReppowd (const TabVecRep& tv, const double& exp) { uInt nr = tv.nelements(); TabVecTemp& vec = *(TabVecTemp*)tv.newVec(); T tmp; for (uInt i=0; i T tabVecRepsum (const TabVecRep& tv) { uInt nr = tv.nelements(); if (nr == 0) { throw(ArrayError("T sum(const TabVecRep&) - " "TabVecRep has no elements")); } T tmp, res; tv.getVal (0, res); for (uInt i=1; i T tabVecRepproduct (const TabVecRep& tv) { uInt nr = tv.nelements(); if (nr == 0) { throw(ArrayError("T product(const TabVecRep&) - " "TabVecRep has no elements")); } T tmp, res; tv.getVal (0, res); for (uInt i=1; i T tabVecRepinnerproduct (const TabVecRep& tvl, const TabVecRep& tvr) { uInt nr = tvr.nelements(); tvl.validateConformance(nr); T res = 0; for (uInt i=0; i T tabVecRepnorm (const TabVecRep& tv) { uInt nr = tv.nelements(); T tmp; T res = 0; for (uInt 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-2.4.1/tables/Tables/TVecScaCol.h000066400000000000000000000067761321422335000200740ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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). uInt nelem() const; // Get a value. T value (uInt index) const; // Get a value. void getVal (uInt index, T&) const; // Put a value. void putVal (uInt 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-2.4.1/tables/Tables/TVecScaCol.tcc000066400000000000000000000051031321422335000203750ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 uInt TabVecScaCol::nelem() const { return colPtr_p->nrow(); } template T TabVecScaCol::value (uInt i) const { return (*colPtr_p)(i); } template void TabVecScaCol::getVal (uInt i, T& val) const { colPtr_p->get (i, val); } template void TabVecScaCol::putVal (uInt i, const T& val) { colPtr_p->put (i, val); } template void TabVecScaCol::set (const T& val) { uInt nrrow = colPtr_p->nrow(); for (uInt i=0; iput (i, val); } } } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/tables/Tables/TVecTemp.h000066400000000000000000000104041321422335000176140ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef TABLES_TVECTEMP_H #define TABLES_TVECTEMP_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations template class Vector; // // 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 (uInt leng); // Destruct the object. ~TabVecTemp(); // Return a reference to a value. inline const T& operator() (uInt index) const; // Return a reference to a value. inline T& operator() (uInt index); // Get a value (virtual function). T value (uInt index) const; // Get a value (virtual function). void getVal (uInt index, T&) const; // Put a value (virtual function). void putVal (uInt 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() (uInt index) const { return (*vecPtr_p)(index); } template inline T& TabVecTemp::operator() (uInt index) { return (*vecPtr_p)(index); } } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-2.4.1/tables/Tables/TVecTemp.tcc000066400000000000000000000046401321422335000201430ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (uInt 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 (uInt i) const { return (*vecPtr_p)(i); } template void TabVecTemp::getVal (uInt i, T& val) const { val = (*vecPtr_p)(i); } template void TabVecTemp::putVal (uInt i, const T& val) { (*vecPtr_p)(i) = val; } template void TabVecTemp::set (const T& val) { vecPtr_p->set (val); } } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/tables/Tables/TabPath.cc000066400000000000000000000045271321422335000176170ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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-2.4.1/tables/Tables/TabPath.h000066400000000000000000000050551321422335000174560ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/tables/Tables/TabVecLogic.h000066400000000000000000000177451321422335000202660ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/tables/Tables/TabVecLogic.tcc000066400000000000000000000027651321422335000206040ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/tables/Tables/TabVecMath.h000066400000000000000000000352741321422335000201170ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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&, Int start, Int 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&, Int 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, Int start, Int inc) { tabVecRepindgen (tv.tabVec(), start, inc); } template inline void indgen (TableVector& tv, Int start) { tabVecRepindgen (tv.tabVec(), start, 1); } template inline void indgen (TableVector& tv) { tabVecRepindgen (tv.tabVec(), 0, 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-2.4.1/tables/Tables/TabVecMath.tcc000066400000000000000000000027521321422335000204340ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/tables/Tables/Table.cc000066400000000000000000000750071321422335000173240ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include #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), isCounted_p (True), lastModCounter_p (0) { baseTabPtr_p = new NullTable(); baseTabPtr_p->link(); } Table::Table (const String& name, TableOption option, const TSMOption& tsmOpt) : baseTabPtr_p (0), isCounted_p (True), lastModCounter_p (0) { open (name, "", option, TableLock(), tsmOpt); } Table::Table (const String& name, const TableLock& lockOptions, TableOption option, const TSMOption& tsmOpt) : baseTabPtr_p (0), isCounted_p (True), lastModCounter_p (0) { open (name, "", option, lockOptions, tsmOpt); } Table::Table (const String& name, const String& type, TableOption option, const TSMOption& tsmOpt) : baseTabPtr_p (0), isCounted_p (True), 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), isCounted_p (True), lastModCounter_p (0) { open (name, type, option, lockOptions, tsmOpt); } Table::Table (Table::TableType type, Table::EndianFormat endianFormat, const TSMOption& tsmOpt) : baseTabPtr_p (0), isCounted_p (True), lastModCounter_p (0) { SetupNewTable newtab("", TableDesc(), Table::Scratch); if (type == Table::Memory) { baseTabPtr_p = new MemoryTable (newtab, 0, False); } else { baseTabPtr_p = new PlainTable (newtab, 0, False, TableLock(), endianFormat, tsmOpt); } baseTabPtr_p->link(); } Table::Table (SetupNewTable& newtab, uInt nrrow, Bool initialize, Table::EndianFormat endianFormat, const TSMOption& tsmOpt) : baseTabPtr_p (0), isCounted_p (True), lastModCounter_p (0) { baseTabPtr_p = new PlainTable (newtab, nrrow, initialize, TableLock(), endianFormat, tsmOpt); baseTabPtr_p->link(); } Table::Table (SetupNewTable& newtab, Table::TableType type, uInt nrrow, Bool initialize, Table::EndianFormat endianFormat, const TSMOption& tsmOpt) : baseTabPtr_p (0), isCounted_p (True), lastModCounter_p (0) { if (type == Table::Memory) { baseTabPtr_p = new MemoryTable (newtab, nrrow, initialize); } else { baseTabPtr_p = new PlainTable (newtab, nrrow, initialize, TableLock(), endianFormat, tsmOpt); } baseTabPtr_p->link(); } Table::Table (SetupNewTable& newtab, Table::TableType type, const TableLock& lockOptions, uInt nrrow, Bool initialize, Table::EndianFormat endianFormat, const TSMOption& tsmOpt) : baseTabPtr_p (0), isCounted_p (True), lastModCounter_p (0) { if (type == Table::Memory) { baseTabPtr_p = new MemoryTable (newtab, nrrow, initialize); } else { baseTabPtr_p = new PlainTable (newtab, nrrow, initialize, lockOptions, endianFormat, tsmOpt); } baseTabPtr_p->link(); } Table::Table (SetupNewTable& newtab, TableLock::LockOption lockOption, uInt nrrow, Bool initialize, Table::EndianFormat endianFormat, const TSMOption& tsmOpt) : baseTabPtr_p (0), isCounted_p (True), lastModCounter_p (0) { baseTabPtr_p = new PlainTable (newtab, nrrow, initialize, TableLock(lockOption), endianFormat, tsmOpt); baseTabPtr_p->link(); } Table::Table (SetupNewTable& newtab, const TableLock& lockOptions, uInt nrrow, Bool initialize, Table::EndianFormat endianFormat, const TSMOption& tsmOpt) : baseTabPtr_p (0), isCounted_p (True), lastModCounter_p (0) { baseTabPtr_p = new PlainTable (newtab, nrrow, initialize, lockOptions, endianFormat, tsmOpt); baseTabPtr_p->link(); } Table::Table (const Block
      • & tables, const Block& subTables, const String& subDirName) : baseTabPtr_p (0), isCounted_p (True), lastModCounter_p (0) { Block btab(tables.nelements()); for (uInt i=0; ilink(); } Table::Table (const Block& tableNames, const Block& subTables, TableOption option, const TSMOption& tsmOpt, const String& subDirName) : baseTabPtr_p (0), isCounted_p (True), lastModCounter_p (0) { baseTabPtr_p = new ConcatTable (tableNames, subTables, subDirName, option, TableLock(), tsmOpt); baseTabPtr_p->link(); } Table::Table (const Block& tableNames, const Block& subTables, const TableLock& lockOptions, TableOption option, const TSMOption& tsmOpt) : baseTabPtr_p (0), isCounted_p (True), lastModCounter_p (0) { baseTabPtr_p = new ConcatTable (tableNames, subTables, String(), option, lockOptions, tsmOpt); baseTabPtr_p->link(); } Table::Table (BaseTable* btp, Bool countIt) : baseTabPtr_p (btp), isCounted_p (countIt), lastModCounter_p (0) { if (isCounted_p && baseTabPtr_p != 0) { baseTabPtr_p->link(); } } Table::Table (const Table& that) : baseTabPtr_p (that.baseTabPtr_p), isCounted_p (that.isCounted_p), lastModCounter_p (that.lastModCounter_p) { if (isCounted_p && baseTabPtr_p != 0) { baseTabPtr_p->link(); } } Table::~Table() { if (isCounted_p && baseTabPtr_p != 0) { #ifdef CASACORE_UNLOCK_TABLE_ON_DESTRUCT unlock(); #endif BaseTable::unlink (baseTabPtr_p); } } Table& Table::operator= (const Table& that) { if (isCounted_p && baseTabPtr_p != 0) { BaseTable::unlink (baseTabPtr_p); } baseTabPtr_p = that.baseTabPtr_p; isCounted_p = that.isCounted_p; lastModCounter_p = that.lastModCounter_p; if (isCounted_p && baseTabPtr_p != 0) { baseTabPtr_p->link(); } return *this; } Table Table::openTable (const String& tableName, TableOption option, const TSMOption& tsmOption) { return openTable (tableName, TableLock(), option, tsmOption); } Table Table::openTable (const String& tableName, const TableLock& lockOptions, TableOption option, const TSMOption& tsmOption) { // See if the table can be opened as such. if (Table::isReadable(tableName)) { return Table(tableName, lockOptions, option, tsmOption); } // Try to open the table using subtables by splitting at :: Table tab; String name = tableName; String msg; int j = name.index("::"); if (j >= 0) { String tabName (name.before(j)); name = name.after(j+1); if (Table::isReadable (tabName)) { tab = Table(tabName, lockOptions, option, tsmOption); while (! name.empty()) { j = name.index("::"); if (j >= 0) { tabName = name.before(j); name = name.after(j+1); } else { tabName = name; name = String(); } if (! tab.keywordSet().isDefined(tabName)) { msg = " (subtable " + tabName + " is unknown)"; tab = Table(); break; } tab = tab.keywordSet().asTable (tabName); } } } if (tab.isNull()) { throw TableError ("Table " + tableName + " does not exist" + msg); } return tab; } Block Table::getPartNames (Bool recursive) const { Block names; baseTabPtr_p->getPartNames (names, recursive); return names; } void Table::closeSubTables() const { return keywordSet().closeTables(); } Bool Table::canDeleteTable (const String& tableName, Bool checkSubTables) { String message; return canDeleteTable (message, tableName, checkSubTables); } Bool Table::canDeleteTable (String& message, const String& tableName, Bool checkSubTables) { String tabName = Path(tableName).absoluteName(); if (! isWritable (tabName)) { message = "table is not writable"; return False; } if (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; } void Table::deleteTable (const String& tableName, Bool checkSubTables) { // Escape from attempt to delete a nameless "table" // because absolute path handling below is potentially // catastrophic! if (tableName.empty()) { throw TableError ("Empty string provided for tableName; will not attempt delete."); } 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); } 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 StManColumn::isNativeDataType (dtype); } //# The logic is similar to that in open. uInt Table::getLayout (TableDesc& desc, const String& tableName) { String tabName = Path(tableName).absoluteName(); uInt nrow, format; String tp; AipsIO ios (Table::fileName(tabName)); ios.getstart ("Table"); ios >> nrow; 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 ("Table::getLayout: unknown table kind " + tp)); } ios.close(); return nrow; } 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) { baseTabPtr_p = btp; }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. baseTabPtr_p = makeBaseTable (absName, type, tableOption, lockOptions, tsmOpt, True, 0); } baseTabPtr_p->link(); if (deleteOpt) { markForDelete(); } } BaseTable* Table::makeBaseTable (const String& name, const String& type, int tableOption, const TableLock& lockOptions, const TSMOption& tsmOpt, Bool addToCache, uInt locknr) { BaseTable* baseTabPtr = 0; //# 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"); uInt nrrow, format; ios >> nrrow; ios >> format; ios >> tp; if (tp == "PlainTable") { baseTabPtr = new PlainTable (ios, version, name, type, nrrow, tableOption, lockOptions, tsmOpt, addToCache, locknr); } else if (tp == "RefTable") { baseTabPtr = new RefTable (ios, name, nrrow, tableOption, lockOptions, tsmOpt); } else if (tp == "ConcatTable") { baseTabPtr = new ConcatTable (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)); } Vector Table::rowNumbers () const { return baseTabPtr_p->rowNumbers(); } Vector Table::rowNumbers (const Table& that, Bool tryFast) const { Vector thisRows(rowNumbers()); const uInt highValue = 4294967295u; // 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. uInt nrthat = thatRows.nelements(); uInt 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; uInt* 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 uInt* thatRowData = thatRows.getStorage (deleteThat); for (uInt i=0; i maxv) { thisRowData[i] = highValue; } else { thisRowData[i] = rownrsData[thisRowData[i]]; } } thisRows.putStorage (thisRowData, deleteThis); const uInt *dummy(rownrsData); // Need to const the pointer to get // by the SGI compiler. 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 uInt* r1 = v1.getStorage (d1); const uInt* r2 = v2.getStorage (d2); uInt* routc = rows.getStorage (d3); uInt* rout = routc; uInt i1=0; uInt 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 (*this, columnName, fieldNames); } TableExprNode Table::col (const String& columnName, const Vector& fieldNames) const { return TableExprNode::newColumnNode (*this, columnName, fieldNames); } //# Create an expression node for either a keyword or column. TableExprNode Table::keyCol (const String& name, const Vector& fieldNames) const { if (tableDesc().isColumn (name)) { return col (name, fieldNames); }else{ uInt nr = fieldNames.nelements(); Vector names (nr + 1); names (Slice(1,nr)) = fieldNames; names(0) = name; return key (names); } } TableExprNode Table::nodeRownr(uInt origin) const { return TableExprNode::newRownrNode (*this, origin); } TableExprNode Table::nodeRandom () const { return TableExprNode::newRandomNode (*this); } //# Select rows based on an expression. Table Table::operator() (const TableExprNode& expr, uInt maxRow, uInt offset) const { return Table (baseTabPtr_p->select (expr, maxRow, offset)); } //# Select rows based on row numbers. Table Table::operator() (const Vector& 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(); 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 (AipsError& 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(); ios << " columns, ", ios << tab.nrow(); ios << " 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; } } } //# NAMESPACE CASACORE - END casacore-2.4.1/tables/Tables/Table.h000066400000000000000000001530421321422335000171620ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef TABLES_TABLE_H #define TABLES_TABLE_H //# Includes #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class SetupNewTable; class TableDesc; class ColumnDesc; class TableRecord; class Record; class TableExprNode; class DataManager; class IPosition; template class Vector; template class Block; template class CountedPtr; // // 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. // // 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 //
        // 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). // // 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). // // 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"); // uInt nrrow = myTable.nrow(); // for (uInt 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&, uInt nrrow = 0, Bool initialize = False, EndianFormat = Table::AipsrcEndian, const TSMOption& = TSMOption()); Table (SetupNewTable&, TableType, uInt nrrow = 0, Bool initialize = False, EndianFormat = Table::AipsrcEndian, const TSMOption& = TSMOption()); Table (SetupNewTable&, TableType, const TableLock& lockOptions, uInt nrrow = 0, Bool initialize = False, EndianFormat = Table::AipsrcEndian, const TSMOption& = TSMOption()); Table (SetupNewTable&, TableLock::LockOption, uInt nrrow = 0, Bool initialize = False, EndianFormat = Table::AipsrcEndian, const TSMOption& = TSMOption()); Table (SetupNewTable&, const TableLock& lockOptions, uInt nrrow = 0, Bool initialize = False, EndianFormat = Table::AipsrcEndian, const TSMOption& = TSMOption()); // // 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&); // 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"); // // // static Table openTable (const String& tableName, TableOption = Table::Old, const TSMOption& = TSMOption()); static Table openTable (const String& tableName, const TableLock& lockOptions, TableOption = Table::Old, const TSMOption& = TSMOption()); // // 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 the root table of this table the same as that of the other one? Bool isSameRoot (const Table& other) const; // 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 checkSubTables is set, it also checks if // a subtable is not open in another process. // static Bool canDeleteTable (const String& tableName, Bool checkSubTables=False); static Bool canDeleteTable (String& message, const String& tableName, 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. static void deleteTable (const String& tableName, Bool checkSubTables=False); // 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); // 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). //
        An exception is thrown if the table does not exist. static uInt 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. static TableInfo tableInfo (const String& tableName); // 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(). uInt 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 (uInt 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 (uInt rownr); void removeRow (const Vector& 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; TableExprNode keyCol (const String& name, 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 (uInt 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&, uInt maxRow=0, uInt 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 Vector& 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 CountedPtrs of compare objects. // A null CountedPtr 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. Vector 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); // Vector 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 //
        Vector rownrs = subset.rowNumbers() // does not give the row numbers in tab, but in the root table // (which is probably not what you want). Vector 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; protected: BaseTable* baseTabPtr_p; //# ptr to table representation //# The isCounted_p flag is normally true. //# Only for internally used Table objects (i.e. in the DataManager) //# this flag is False, otherwise a mutual dependency would exist. //# The DataManager has a Table object, which gets deleted by the //# DataManager destructor. The DataManager gets deleted by the //# PlainTable destructor, which gets called when the last Table //# object gets destructed. That would never be the case if this //# internally used Table object was counted. Bool isCounted_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 BaseTable*. // By default the object gets counted. Table (BaseTable*, Bool countIt = True); // 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 BaseTable* 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; // 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. 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 uInt 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 TableInfo Table::tableInfo (const String& tableName) { return BaseTable::tableInfo (tableName); } 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 (uInt nrrow, Bool initialize) { baseTabPtr_p->addRow (nrrow, initialize); } inline void Table::removeRow (uInt rownr) { baseTabPtr_p->removeRow (rownr); } inline void Table::removeRow (const Vector& 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-2.4.1/tables/Tables/TableAttr.cc000066400000000000000000000047451321422335000201600ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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-2.4.1/tables/Tables/TableAttr.h000066400000000000000000000073561321422335000200230ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/tables/Tables/TableCache.cc000066400000000000000000000140461321422335000202440ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN TableCache::TableCache() : tableMap_p(static_cast(0)) {} TableCache::~TableCache() {} PlainTable* TableCache::operator() (const String& tableName) const { ScopedMutexLock sc(itsMutex); return getTable (tableName); } PlainTable* TableCache::getTable (const String& tableName) const { PlainTable** ptr = (PlainTable**)(tableMap_p.isDefined (tableName)); if (ptr) { return *ptr; } return 0; } void TableCache::define (const String& tableName, PlainTable* tab) { ScopedMutexLock sc(itsMutex); tableMap_p.define (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). ScopedMutexLock sc(itsMutex); if (tableMap_p.ndefined() > 0) { try { tableMap_p.remove (tableName); } catch (AipsError&) { // 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) { ScopedMutexLock sc(itsMutex); if (tableMap_p.isDefined (oldName)) { tableMap_p.rename (newName, oldName); } } uInt TableCache::nAutoLocks() { ScopedMutexLock sc(itsMutex); uInt n=0; uInt ntab = tableMap_p.ndefined(); for (uInt i=0; i(tableMap_p.getVal(i)); 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) { ScopedMutexLock sc(itsMutex); uInt ntab = tableMap_p.ndefined(); for (uInt i=0; i(tableMap_p.getVal(i)); 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 { ScopedMutexLock sc(itsMutex); uInt ntab = tableMap_p.ndefined(); Vector names(ntab); for (uInt i=0; i(tableMap_p.getVal(i)); names[i] = table.tableName(); } return names; } Vector TableCache::getLockedTables (FileLocker::LockType lockType, int lockOption) { ScopedMutexLock sc(itsMutex); vector names; uInt ntab = tableMap_p.ndefined(); for (uInt i=0; i(tableMap_p.getVal(i)); 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) { ScopedMutexLock 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-2.4.1/tables/Tables/TableCache.h000066400000000000000000000135571321422335000201140ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef TABLES_TABLECACHE_H #define TABLES_TABLECACHE_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class PlainTable; class TableLock; template class Vector; // // 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(); // 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: // The copy constructor is forbidden. TableCache (const TableCache&); // The assignment operator is forbidden. TableCache& operator= (const TableCache&); // 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. SimpleOrderedMap tableMap_p; //# A mutex to synchronize access to the cache. mutable Mutex itsMutex; }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/tables/Tables/TableColumn.cc000066400000000000000000000315311321422335000204740ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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, False); } Bool TableColumn::asBool (uInt rownr) const { TABLECOLUMNCHECKROW(rownr); Bool value; baseColPtr_p->getScalar (rownr, value); return value; } uChar TableColumn::asuChar (uInt rownr) const { TABLECOLUMNCHECKROW(rownr); uChar value; baseColPtr_p->getScalar (rownr, value); return value; } Short TableColumn::asShort (uInt rownr) const { TABLECOLUMNCHECKROW(rownr); Short value; baseColPtr_p->getScalar (rownr, value); return value; } uShort TableColumn::asuShort (uInt rownr) const { TABLECOLUMNCHECKROW(rownr); uShort value; baseColPtr_p->getScalar (rownr, value); return value; } Int TableColumn::asInt (uInt rownr) const { TABLECOLUMNCHECKROW(rownr); Int value; baseColPtr_p->getScalar (rownr, value); return value; } uInt TableColumn::asuInt (uInt rownr) const { TABLECOLUMNCHECKROW(rownr); uInt value; baseColPtr_p->getScalar (rownr, value); return value; } float TableColumn::asfloat (uInt rownr) const { TABLECOLUMNCHECKROW(rownr); float value; baseColPtr_p->getScalar (rownr, value); return value; } double TableColumn::asdouble (uInt rownr) const { TABLECOLUMNCHECKROW(rownr); double value; baseColPtr_p->getScalar (rownr, value); return value; } Complex TableColumn::asComplex (uInt rownr) const { TABLECOLUMNCHECKROW(rownr); Complex value; baseColPtr_p->getScalar (rownr, value); return value; } DComplex TableColumn::asDComplex (uInt rownr) const { TABLECOLUMNCHECKROW(rownr); DComplex value; baseColPtr_p->getScalar (rownr, value); return value; } String TableColumn::asString (uInt rownr) const { TABLECOLUMNCHECKROW(rownr); String value; baseColPtr_p->getScalar (rownr, value); return value; } void TableColumn::put (uInt thisRownr, const TableColumn& that, uInt 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 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)->get (thatRownr, &array); vh = ValueHolder (array); } break; case TpUChar: { Array array(shape); baseColPtr(that)->get (thatRownr, &array); vh = ValueHolder (array); } break; case TpShort: { Array array(shape); baseColPtr(that)->get (thatRownr, &array); vh = ValueHolder (array); } break; case TpUShort: { Array array(shape); baseColPtr(that)->get (thatRownr, &array); vh = ValueHolder (array); } break; case TpInt: { Array array(shape); baseColPtr(that)->get (thatRownr, &array); vh = ValueHolder (array); } break; case TpUInt: { Array array(shape); baseColPtr(that)->get (thatRownr, &array); vh = ValueHolder (array); } break; case TpFloat: { Array array(shape); baseColPtr(that)->get (thatRownr, &array); vh = ValueHolder (array); } break; case TpDouble: { Array array(shape); baseColPtr(that)->get (thatRownr, &array); vh = ValueHolder (array); } break; case TpComplex: { Array array(shape); baseColPtr(that)->get (thatRownr, &array); vh = ValueHolder (array); } break; case TpDComplex: { Array array(shape); baseColPtr(that)->get (thatRownr, &array); vh = ValueHolder (array); } break; case TpString: { Array array(shape); baseColPtr(that)->get (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->put (thisRownr, &arr); } break; case TpUChar: { Array arr (vh.asArrayuChar()); baseColPtr_p->put (thisRownr, &arr); } break; case TpShort: { Array arr (vh.asArrayShort()); baseColPtr_p->put (thisRownr, &arr); } break; case TpUShort: { Array arr (vh.asArrayuShort()); baseColPtr_p->put (thisRownr, &arr); } break; case TpInt: { Array arr (vh.asArrayInt()); baseColPtr_p->put (thisRownr, &arr); } break; case TpUInt: { Array arr (vh.asArrayuInt()); baseColPtr_p->put (thisRownr, &arr); } break; case TpFloat: { Array arr (vh.asArrayFloat()); baseColPtr_p->put (thisRownr, &arr); } break; case TpDouble: { Array arr (vh.asArrayDouble()); baseColPtr_p->put (thisRownr, &arr); } break; case TpComplex: { Array arr (vh.asArrayComplex()); baseColPtr_p->put (thisRownr, &arr); } break; case TpDComplex: { Array arr (vh.asArrayDComplex()); baseColPtr_p->put (thisRownr, &arr); } break; case TpString: { Array arr (vh.asArrayString()); baseColPtr_p->put (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(); uInt nrrow = nrow(); if (nrrow != that.nrow()) { throw (TableConformanceError ("TableColumn::putColumn")); } for (uInt i=0; itableName() + " is not writable"); } Bool TableColumn::hasContent (uInt 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, 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. uInt 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 (uInt 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 (uInt rownr=0) const; // Get the #dimensions of an array in a particular cell. uInt ndim (uInt rownr) const { TABLECOLUMNCHECKROW(rownr); return baseColPtr_p->ndim (rownr); } // Get the shape of an array in a particular cell. IPosition shape (uInt rownr) const { TABLECOLUMNCHECKROW(rownr); return baseColPtr_p->shape (rownr); } // Get the tile shape of an array in a particular cell. IPosition tileShape (uInt 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 (uInt rownr, Bool& value) const { TABLECOLUMNCHECKROW(rownr); baseColPtr_p->getScalar (rownr, value); } void getScalar (uInt rownr, uChar& value) const { TABLECOLUMNCHECKROW(rownr); baseColPtr_p->getScalar (rownr, value); } void getScalar (uInt rownr, Short& value) const { TABLECOLUMNCHECKROW(rownr); baseColPtr_p->getScalar (rownr, value); } void getScalar (uInt rownr, uShort& value) const { TABLECOLUMNCHECKROW(rownr); baseColPtr_p->getScalar (rownr, value); } void getScalar (uInt rownr, Int& value) const { TABLECOLUMNCHECKROW(rownr); baseColPtr_p->getScalar (rownr, value); } void getScalar (uInt rownr, uInt& value) const { TABLECOLUMNCHECKROW(rownr); baseColPtr_p->getScalar (rownr, value); } void getScalar (uInt rownr, Int64& value) const { TABLECOLUMNCHECKROW(rownr); baseColPtr_p->getScalar (rownr, value); } void getScalar (uInt rownr, float& value) const { TABLECOLUMNCHECKROW(rownr); baseColPtr_p->getScalar (rownr, value); } void getScalar (uInt rownr, double& value) const { TABLECOLUMNCHECKROW(rownr); baseColPtr_p->getScalar (rownr, value); } void getScalar (uInt rownr, Complex& value) const { TABLECOLUMNCHECKROW(rownr); baseColPtr_p->getScalar (rownr, value); } void getScalar (uInt rownr, DComplex& value) const { TABLECOLUMNCHECKROW(rownr); baseColPtr_p->getScalar (rownr, value); } void getScalar (uInt 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 (uInt rownr) const; uChar asuChar (uInt rownr) const; Short asShort (uInt rownr) const; uShort asuShort (uInt rownr) const; Int asInt (uInt rownr) const; uInt asuInt (uInt rownr) const; float asfloat (uInt rownr) const; double asdouble (uInt rownr) const; Complex asComplex (uInt rownr) const; DComplex asDComplex (uInt rownr) const; String asString (uInt 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 (uInt rownr, Bool* value, const String&) const { TABLECOLUMNCHECKROW(rownr); baseColPtr_p->getScalar (rownr,*value); } void getScalarValue (uInt rownr, uChar* value, const String&) const { TABLECOLUMNCHECKROW(rownr); baseColPtr_p->getScalar (rownr,*value); } void getScalarValue (uInt rownr, Short* value, const String&) const { TABLECOLUMNCHECKROW(rownr); baseColPtr_p->getScalar (rownr,*value); } void getScalarValue (uInt rownr, uShort* value, const String&) const { TABLECOLUMNCHECKROW(rownr); baseColPtr_p->getScalar (rownr,*value); } void getScalarValue (uInt rownr, Int* value, const String&) const { TABLECOLUMNCHECKROW(rownr); baseColPtr_p->getScalar (rownr,*value); } void getScalarValue (uInt rownr, uInt* value, const String&) const { TABLECOLUMNCHECKROW(rownr); baseColPtr_p->getScalar (rownr,*value); } void getScalarValue (uInt rownr, float* value, const String&) const { TABLECOLUMNCHECKROW(rownr); baseColPtr_p->getScalar (rownr,*value); } void getScalarValue (uInt rownr, double* value, const String&) const { TABLECOLUMNCHECKROW(rownr); baseColPtr_p->getScalar (rownr,*value); } void getScalarValue (uInt rownr, Complex* value, const String&) const { TABLECOLUMNCHECKROW(rownr); baseColPtr_p->getScalar (rownr,*value); } void getScalarValue (uInt rownr, DComplex* value, const String&) const { TABLECOLUMNCHECKROW(rownr); baseColPtr_p->getScalar (rownr,*value); } void getScalarValue (uInt rownr, String* value, const String&) const { TABLECOLUMNCHECKROW(rownr); baseColPtr_p->getScalar (rownr,*value); } void getScalarValue (uInt 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 (uInt 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 (uInt thisRownr, const TableColumn& that, uInt 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 (uInt rownr, const Bool& value) { TABLECOLUMNCHECKROW(rownr); baseColPtr_p->putScalar (rownr, value); } void putScalar (uInt rownr, const uChar& value) { TABLECOLUMNCHECKROW(rownr); baseColPtr_p->putScalar (rownr, value); } void putScalar (uInt rownr, const Short& value) { TABLECOLUMNCHECKROW(rownr); baseColPtr_p->putScalar (rownr, value); } void putScalar (uInt rownr, const uShort& value) { TABLECOLUMNCHECKROW(rownr); baseColPtr_p->putScalar (rownr, value); } void putScalar (uInt rownr, const Int& value) { TABLECOLUMNCHECKROW(rownr); baseColPtr_p->putScalar (rownr, value); } void putScalar (uInt rownr, const uInt& value) { TABLECOLUMNCHECKROW(rownr); baseColPtr_p->putScalar (rownr, value); } void putScalar (uInt rownr, const float& value) { TABLECOLUMNCHECKROW(rownr); baseColPtr_p->putScalar (rownr, value); } void putScalar (uInt rownr, const double& value) { TABLECOLUMNCHECKROW(rownr); baseColPtr_p->putScalar (rownr, value); } void putScalar (uInt rownr, const Complex& value) { TABLECOLUMNCHECKROW(rownr); baseColPtr_p->putScalar (rownr, value); } void putScalar (uInt rownr, const DComplex& value) { TABLECOLUMNCHECKROW(rownr); baseColPtr_p->putScalar (rownr, value); } void putScalar (uInt rownr, const String& value) { TABLECOLUMNCHECKROW(rownr); baseColPtr_p->putScalar (rownr, value); } void putScalar (uInt 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 (uInt 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-2.4.1/tables/Tables/TableCopy.cc000066400000000000000000000213241321422335000201500ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# Includes #include #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, uInt startout, uInt startin, uInt 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 (uInt 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) { // Use existing column description and give it the new name. ColumnDesc cd(fromTable.tableDesc()[fromColumn]); cd.setName (newColumn); doCloneColumn (fromTable, fromColumn, toTable, cd, dataManagerName); } void TableCopy::doCloneColumn (const Table& fromTable, const String& fromColumn, Table& toTable, const ColumnDesc& newColumn, const String& dataManagerName) { // Use existing column description and give it the new name. TableDesc td; td.addColumn (newColumn); // Get datamanager info of DATA column. Block selcol(1); selcol[0] = fromColumn; // Do the selection to only get dminfo of DATA Table t1(fromTable.project (selcol)); Record dminfo = t1.dataManagerInfo(); // Set the datamananger 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 (uInt 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, uInt startout, uInt startin, uInt 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 and data manager as the // from column. It has to get 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()); // 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()); // 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); } // 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) { DataManInfo::adjustTSM (tabDesc, dminfo); } // Replace non-writable storage managers by StandardStMan. This is needed // for special storage managers like LofarStMan. static Record adjustStMan (const Record& dminfo) { return DataManInfo::adjustStMan (dminfo, "StandardStMan"); } // 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) { DataManInfo::setTiledStMan (dminfo, columns, dmType, dmName, defaultTileShape); } // Remove the columns from the dminfo record and return a vector with 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 // have to match, so "Tiled" matches all tiled storagemanagers. static Vector removeDminfoColumns (Record& dminfo, const Vector& columns, const String& keepType= String()) { return DataManInfo::removeDminfoColumns (dminfo, columns, keepType); } // 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) { DataManInfo::adjustDesc (tabDesc, dminfo); } private: static void doCloneColumn (const Table& fromTable, const String& fromColumn, Table& toTable, const ColumnDesc& newColumn, const String& dataManagerName); }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-2.4.1/tables/Tables/TableCopy.tcc000066400000000000000000000105101321422335000203270ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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) { // 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); } 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 (uInt 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. SimpleOrderedMap old2new("", 1); // 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 SimpleOrderedMap& 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 0) { dataNames.resize (nr, True); nr = 0; for (uInt j=0; j& 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-2.4.1/tables/Tables/TableDesc.h000066400000000000000000000573061321422335000177670ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef TABLES_TABLEDESC_H #define TABLES_TABLEDESC_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class TableRecord; class TableAttr; class TabPath; template class Vector; // // 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. // // 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(); // 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 SimpleOrderedMap& 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 // 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? // Declaring it private, makes it unusable. TableDesc& operator= (const TableDesc&); // 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-2.4.1/tables/Tables/TableError.cc000066400000000000000000000142461321422335000203340ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 () throw() {} TableInternalError::TableInternalError (const String& str,Category c) : TableError("Internal Table error: " + str,c) {} TableInternalError::~TableInternalError () throw() {} 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 () throw() {} 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 () throw() {} TableNoDir::TableNoDir (const String& name,Category c) : TableError(name + " is not a directory",c) {} TableNoDir::~TableNoDir () throw() {} 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 () throw() {} TableDescNoName::TableDescNoName (Category c) : TableError ("No name for table description",c) {} TableDescNoName::~TableDescNoName () throw() {} TableInvOpt::TableInvOpt (const String& cl, const String& str, Category c) : TableError ("Invalid " + cl + " option: " + str,c) {} TableInvOpt::~TableInvOpt () throw() {} 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 () throw() {} TableInvColumnDesc::TableInvColumnDesc (const String& name, const String& msg,Category c) : TableError("Invalid description of column " + name + ": " + msg,c) {} TableInvColumnDesc::~TableInvColumnDesc () throw() {} TableInvHyperDesc::TableInvHyperDesc (const String& name, const String& msg,Category c) : TableError("Invalid description of hypercolumn " + name + ": " + msg,c) {} TableInvHyperDesc::~TableInvHyperDesc () throw() {} TableUnknownDesc::TableUnknownDesc (const String& name,Category c) : TableError("ColumnDesc class " + name + " unknown to ColumnDesc::register",c) {} TableUnknownDesc::~TableUnknownDesc () throw() {} 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 () throw() {} TableInvOper::TableInvOper (Category c) : TableError ("Invalid Table operation",c) {} TableInvOper::TableInvOper (const String& s,Category c) : TableError ("Invalid Table operation: " + s,c) {} TableInvOper::~TableInvOper () throw() {} TableArrayConformanceError::TableArrayConformanceError (const String& s,Category c) : TableError (s + ": Table array conformance error",c) {} TableArrayConformanceError::~TableArrayConformanceError () throw() {} TableConformanceError::TableConformanceError (const String& s,Category c) : TableError (s + ": Table conformance error (#rows mismatch)",c) {} TableConformanceError::~TableConformanceError () throw() {} TableInvSort::TableInvSort (Category c) : TableError ("Invalid table sort",c) {} TableInvSort::TableInvSort (const String& s,Category c) : TableError ("Invalid table sort: " + s,c) {} TableInvSort::~TableInvSort () throw() {} TableInvLogic::TableInvLogic (Category c) : TableError ("Tables in logical operation have different roots",c) {} TableInvLogic::~TableInvLogic () throw() {} 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 () throw() {} TableVectorNonConform::TableVectorNonConform (Category c) : TableError ("Shapes of table vectors are not conformant",c) {} TableVectorNonConform::~TableVectorNonConform () throw() {} 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 () throw() {} 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 () throw() {} } //# NAMESPACE CASACORE - END casacore-2.4.1/tables/Tables/TableError.h000066400000000000000000000372441321422335000202010ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef TABLES_TABLEERROR_H #define TABLES_TABLEERROR_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 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 () throw(); }; // // 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 () throw(); }; // // 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 () throw(); }; // // 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 () throw(); }; // // 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 () throw(); }; // // 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 () throw(); }; // 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 () throw(); }; // // 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 () throw(); }; // // 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 () throw(); }; // // 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 () throw(); }; // // 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 () throw(); }; // // 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 () throw(); }; // // 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 () throw(); }; // // 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 () throw(); }; // // 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); ~TableArrayConformanceError () throw(); }; // // 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 () throw(); }; // // 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 () throw(); }; // // 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 () throw(); }; // // 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 () throw(); }; // // 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 () throw(); }; // // 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 () throw(); // 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 () throw(); // 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-2.4.1/tables/Tables/TableExprId.h000066400000000000000000000003331321422335000202700ustar00rootroot00000000000000#ifndef TABLES_TABLES_STUB_TABLEEXPRID_H #define TABLES_TABLES_STUB_TABLEEXPRID_H #include #warning "Tables/TableExprId.h has been deprecated; use TaQL/TableExprId.h instead" #endif casacore-2.4.1/tables/Tables/TableIndexProxy.cc000066400000000000000000000101751321422335000213510ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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)); } } } } Int TableIndexProxy::getRowNumber (const Record& key) { Bool found; Int 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) { Vector 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) { Vector 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-2.4.1/tables/Tables/TableIndexProxy.h000066400000000000000000000074061321422335000212160ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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(); // 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. Int 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: // Assignment is forbidden. TableIndexProxy& operator= (const TableIndexProxy&); ColumnsIndex* scaIndex_p; ColumnsIndexArray* arrIndex_p; }; } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/tables/Tables/TableInfo.cc000066400000000000000000000131041321422335000201260ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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-2.4.1/tables/Tables/TableInfo.h000066400000000000000000000164171321422335000200020ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/tables/Tables/TableIter.cc000066400000000000000000000103411321422335000201360ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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) : tabIterPtr_p (0) { tabIterPtr_p = tab.baseTablePtr()->makeIterator (keys, cmpObjs, orders, option); 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-2.4.1/tables/Tables/TableIter.h000066400000000000000000000202411321422335000200000ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 for the given table. // Each iteration step results in a Table containing all // rows in which the values in each given column is equal. // 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. TableIterator (const Table&, const Block& columnNames, const Block >&, const Block& orders, Option = ParSort); // // 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 == 0 ? True : False); } // 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-2.4.1/tables/Tables/TableIterProxy.cc000066400000000000000000000162021321422335000212020ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 TpFloat: case TpDouble: break; case TpString: comps[i] = new CompareNoCase(); 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] = new CompareIntervalInt(iterSteps[i], start); } break; case TpShort: { Short start = ScalarColumn(sortab, columns[i])(0); comps[i] = new CompareIntervalInt(iterSteps[i], start); } break; case TpUShort: { uShort start = ScalarColumn(sortab, columns[i])(0); comps[i] = new CompareIntervalInt(iterSteps[i], start); } break; case TpInt: { Int start = ScalarColumn(sortab, columns[i])(0); comps[i] = new CompareIntervalInt(iterSteps[i], start); } break; case TpUInt: { uInt start = ScalarColumn(sortab, columns[i])(0); comps[i] = new CompareIntervalInt(iterSteps[i], start); } break; case TpFloat: { Float start = ScalarColumn(sortab, columns[i])(0); comps[i] = new CompareIntervalReal(iterSteps[i], start); } break; case TpDouble: { Double start = ScalarColumn(sortab, columns[i])(0); comps[i] = new CompareIntervalReal(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-2.4.1/tables/Tables/TableIterProxy.h000066400000000000000000000117541321422335000210530ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/tables/Tables/TableKeyword.cc000066400000000000000000000134461321422335000206700ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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-2.4.1/tables/Tables/TableKeyword.h000066400000000000000000000177441321422335000205370ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/tables/Tables/TableLock.cc000066400000000000000000000074531321422335000201350ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 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 return False; #endif } } //# NAMESPACE CASACORE - END casacore-2.4.1/tables/Tables/TableLock.h000066400000000000000000000151211321422335000177660ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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. // // 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. 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 was 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-2.4.1/tables/Tables/TableLockData.cc000066400000000000000000000107111321422335000207160ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/tables/Tables/TableLockData.h000066400000000000000000000122571321422335000205670ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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(); // 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: // Copy constructor is forbidden. TableLockData (const TableLockData& that); // Assignment is forbidden. TableLockData& operator= (const TableLockData& that); //# 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-2.4.1/tables/Tables/TableLocker.cc000066400000000000000000000041121321422335000204510ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/tables/Tables/TableLocker.h000066400000000000000000000120341321422335000203150ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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(); // 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: // 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&); TableLocker& operator= (const TableLocker&); // //# Variables. Table itsTable; bool itsHadLock; }; inline Bool TableLocker::hasLock (FileLocker::LockType type) const { return itsTable.hasLock (type); } } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/tables/Tables/TableParse.h000066400000000000000000000003261321422335000201510ustar00rootroot00000000000000#ifndef TABLES_TABLES_STUB_TABLEPARSE_H #define TABLES_TABLES_STUB_TABLEPARSE_H #include #warning "Tables/TableParse.h has been deprecated; use TaQL/TableParse.h instead" #endif casacore-2.4.1/tables/Tables/TableProxy.cc000066400000000000000000002673341321422335000203740ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: TableProxy.cc 21399 2013-11-12 07:55:35Z gervandiepen $ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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 sprintf namespace casacore { //# NAMESPACE CASACORE - BEGIN TableProxy::TableProxy() {} TableProxy::TableProxy (const String& tableName, const Record& lockOptions, int option) { table_p = Table::openTable (tableName, makeLockOptions(lockOptions), Table::TableOption(option)); } TableProxy::TableProxy (const String& tableName, const Record& lockOptions, const String& endianFormat, const String& memType, Int 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). SetupNewTable newtab(tableName, tabdesc, tableName.empty() ? Table::Scratch : Table::New); // Apply a possible dminfo object. newtab.bindCreate (dmInfo); table_p = Table (newtab, type, 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; 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; 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; 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; 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, Int startIn, Int startOut, Int nrow) { Table tableOut = out.table(); if (startOut < 0) { startOut = tableOut.nrow(); } nrow = checkRowColumn (table_p, "", startIn, nrow, 1, "TableProxy::copyRows"); if (startOut > Int(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, 0) || anyGE (rownrs, Int(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.getColumnInt (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 (uInt i=0; i arr; expr.get (i, arr); res.define (String::toString(i), arr.array()); stillSameShape (sameShape, resShape, arr.shape()); } break; case TpInt64: for (uInt i=0; i arr; expr.get (i, arr); res.define (String::toString(i), arr.array()); stillSameShape (sameShape, resShape, arr.shape()); } break; case TpDouble: for (uInt i=0; i arr; expr.get (i, arr); res.define (String::toString(i), arr.array()); stillSameShape (sameShape, resShape, arr.shape()); } break; case TpDComplex: for (uInt i=0; i arr; expr.get (i, arr); res.define (String::toString(i), arr.array()); stillSameShape (sameShape, resShape, arr.shape()); } break; case TpString: for (uInt 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, Bool byColumn, const Record& properties) { RODataManAccessor acc (table_p, name, byColumn); acc.setProperties (properties); } Record TableProxy::getTableDescription (Bool actual, Bool cOrder) { // Get the table description. const TableDesc* tableDescPtr; if (actual) { tableDescPtr = new TableDesc(table_p.actualTableDesc()); } else { tableDescPtr = new TableDesc(table_p.tableDesc()); } Record rec = getTableDesc(*tableDescPtr, cOrder); delete tableDescPtr; return rec; } Record TableProxy::getTableDesc(const TableDesc & tabdesc, Bool cOrder) { Record rec; // Convert columns for (uInt i=0; i TableProxy::getPartNames (Bool recursive) { return Vector(table_p.getPartNames (recursive)); } 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(); } Int 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, Int row) { Int64 nrow = getRowsCheck (columnName, row, 1, 1, "getCell"); return getValueFromTable (columnName, row, nrow, 1, True); } void TableProxy::getCellVH (const String& columnName, Int 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, Int row, const Vector& blc, const Vector& trc, const Vector& inc) { return getCellSliceIP (columnName, row, blc, trc, inc); } void TableProxy::getCellSliceVH (const String& columnName, Int 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, Int 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, Int 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, Int row, Int nrow, Int incr) { Int64 nrows = getRowsCheck (columnName, row, nrow, incr, "getColumn"); return getValueFromTable (columnName, row, nrows, incr, False); } void TableProxy::getColumnVH (const String& columnName, Int row, Int nrow, Int 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, Int row, Int nrow, Int incr) { Int64 nrows = getRowsCheck (columnName, row, nrow, incr, "getVarColumn"); TableColumn tabcol (table_p, columnName); Record rec; char namebuf[16]; 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, Int row, Int nrow, Int 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, Int row, Int nrow, Int 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, Int row, Int nrow, Int 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, Int row, Int nrow, Int 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, Int row, Int nrow, Int 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 (Int 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, Int row, Int nrow, Int 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 (uInt 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, Int 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); Int nrow = checkRowColumn (table_p, columnName, row, 1, 1, "TableProxy::putColumn"); putValueSliceInTable (columnName, slicer, row, nrow, 1, True, value); } Vector TableProxy::getColumnShapeString (const String& columnName, Int rownr, Int nrow, Int 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. Int 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"); } Int 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); Int lastRow(nrow+rownr); for (Int 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 (Int 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.asArrayInt ("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 == "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, 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 == "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 TpUInt: return "uint"; 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); } Int 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. Int 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"); } } Int maxnrow = (tabnrow - rownr + incr - 1) / incr; if (nrow < 0 || nrow > maxnrow) { nrow = maxnrow; } return nrow; } ValueHolder TableProxy::getValueFromTable (const String& colName, Int rownr, Int nrow, Int incr, Bool isCell) { // Exit immediately if no rows have to be done. if (nrow == 0) { return ValueHolder(); } const ColumnDesc& cdesc = table_p.tableDesc().columnDesc(colName); Bool isScalar = cdesc.isScalar(); DataType dtype = cdesc.dataType(); 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 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 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, Int rownr, Int nrow, Int 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"); } 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, Int rownr, Int nrow, Int 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 ValueHolder(); } 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 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, Int rownr, Int nrow, Int 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"); } 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, Int rownr, Int nrow, Int 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 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 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, Int rownr, Int nrow, Int 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 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; template class Vector; 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, int 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, Int firstLine, Int 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, Int startIn, Int startOut, Int 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 (Int 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, Int row, Int nrow, Int incr); void getColumnVH (const String& columnName, Int row, Int nrow, Int incr, const ValueHolder& vh); Record getVarColumn (const String& columnName, Int row, Int nrow, Int 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, Int row, Int nrow, Int incr, const Vector& blc, const Vector& trc, const Vector& inc); ValueHolder getColumnSliceIP (const String& columnName, const IPosition& blc, const IPosition& trc, const IPosition& inc, Int row, Int nrow, Int incr); void getColumnSliceVH (const String& columnName, Int row, Int nrow, Int 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, Int row, Int nrow, Int 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, Int row, Int nrow, Int incr, const ValueHolder&); void putVarColumn (const String& columnName, Int row, Int nrow, Int incr, const Record& values); // // Put some or all value slices into a column in the table. // void putColumnSlice (const String& columnName, Int row, Int nrow, Int 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, Int row, Int nrow, Int 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, Int rownr); // Get a value from a column in the table. ValueHolder getCell (const String& columnName, Int row); void getCellVH (const String& columnName, Int 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, Int row, const Vector& blc, const Vector& trc, const Vector& inc); ValueHolder getCellSliceIP (const String& columnName, Int row, const IPosition& blc, const IPosition& trc, const IPosition& inc); void getCellSliceVH (const String& columnName, Int row, const Vector& blc, const Vector& trc, const Vector& inc, const ValueHolder& vh); void getCellSliceVHIP (const String& columnName, Int 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, Int row, const Vector& blc, const Vector& trc, const Vector& inc, const ValueHolder&); void putCellSliceIP (const String& columnName, Int 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, Int rownr, Int nrow, Int 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. Int 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, // 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, Bool byColumn, const Record& properties); // 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. Int checkRowColumn (Table& table, const String& colName, Int64 rownr, Int64 nrow, Int64 incr, const String& caller); // Get values from the column. // Nrow<0 means till the end of the column. ValueHolder getValueFromTable (const String& colName, Int rownr, Int nrow, Int incr, Bool isCell); void getValueFromTable (const String& colName, Int rownr, Int nrow, Int 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, Int rownr, Int nrow, Int incr, Bool isCell); void getValueSliceFromTable(const String& colName, const Slicer& slicer, Int rownr, Int nrow, Int 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, Int rownr, Int nrow, Int 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, Int rownr, Int nrow, Int 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-2.4.1/tables/Tables/TableRecord.cc000066400000000000000000000347651321422335000204710ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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) { notify (RecordNotice (RecordNotice::DETACH, 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() { const TableRecordRep& oldRep = rep_p.ref(); TableRecordRep& newRep = rep_p.rwRef(); if (&oldRep != &newRep) { notify (RecordNotice (RecordNotice::ACQUIRE, 0)); } 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(); // Restructuring means that all RecordFieldPtr's get invalid. notify (RecordNotice (RecordNotice::DETACH, 0)); 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); // Possible RecordFieldPtr's have to be detached. notify (RecordNotice (RecordNotice::DETACH, 0)); // 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 namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations template class Array; 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 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, 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 (using the Notice classes). //
        // 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 containg '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-2.4.1/tables/Tables/TableRecordRep.cc000066400000000000000000000374471321422335000211400ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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] == 0) { 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-2.4.1/tables/Tables/TableRecordRep.h000066400000000000000000000241641321422335000207720ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #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-2.4.1/tables/Tables/TableRow.cc000066400000000000000000000712011321422335000200040ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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 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 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 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 (uInt 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 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 (uInt 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 (uInt 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 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 (uInt 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 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 (uInt 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 (uInt 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 namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class TableColumn; template class Vector; //

        // 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 (uInt i=0; i col1(row.record(), "col1"); // RORecordFieldPtr col2(row.record(), "col2"); // RORecordFieldPtr > col3(row.record(), "col3"); // for (uInt 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 (uInt 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 (uInt 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 (uInt rownr, const TableRecord& record, Int whichColumn, Int whichField); // Set the switch to reread when the current row has been put. void setReread (uInt 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 (uInt 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 (uInt 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 (uInt 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 (uInt rownr, const TableRecord& record, Bool checkConformance = True); void put (uInt 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 (uInt 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 (uInt rownr) { putRecord (rownr); } } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/tables/Tables/TableRowProxy.cc000066400000000000000000000061221321422335000210460ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (uInt rownr) const { return rorow_p.get(rownr, True).toRecord(); } void TableRowProxy::put (uInt 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-2.4.1/tables/Tables/TableRowProxy.h000066400000000000000000000064271321422335000207200ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (uInt rownr) const; // Put values for the given row. // The given record has to conform the fields in the table row. void put (uInt 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-2.4.1/tables/Tables/TableSyncData.cc000066400000000000000000000121701321422335000207430ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN TableSyncData::TableSyncData() : itsNrrow (0), itsNrcolumn (-1), itsModifyCounter (0), itsTableChangeCounter (0) { itsAipsIO.open (&itsMemIO); } TableSyncData::~TableSyncData() { itsAipsIO.close(); } void TableSyncData::write (uInt 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 i; uInt ndmOld = itsDataManChangeCounter.nelements(); uInt ndmNew = dataManChanged.nelements(); if (ndmNew != ndmOld) { itsDataManChangeCounter.resize (ndmNew, True, True); for (i=ndmOld; i= 0) { itsAipsIO << itsTableChangeCounter; itsAipsIO << itsDataManChangeCounter; } itsAipsIO.putend(); } void TableSyncData::write (uInt nrrow) { itsModifyCounter++; itsNrrow = nrrow; itsNrcolumn = -1; // Now write the data into the memoryIO object. // First clear it. itsMemIO.clear(); itsAipsIO.putstart ("sync" ,1); itsAipsIO << itsNrrow; itsAipsIO << itsNrcolumn; itsAipsIO << itsModifyCounter; itsAipsIO.putend(); } Bool TableSyncData::read (uInt& 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). uInt i; Int nrcol = -1; if (itsMemIO.length() > 0) { itsAipsIO.getstart ("sync"); 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 (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(); // 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 (uInt 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 (uInt 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 (uInt& 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: // Copy constructor is forbidden. TableSyncData (const TableSyncData& that); // Assignment is forbidden. TableSyncData& operator= (const TableSyncData& that); //# Member variables. uInt itsNrrow; Int itsNrcolumn; uInt itsModifyCounter; uInt itsTableChangeCounter; Block itsDataManChangeCounter; MemoryIO itsMemIO; AipsIO itsAipsIO; }; inline MemoryIO& TableSyncData::memoryIO() { return itsMemIO; } inline uInt TableSyncData::getModifyCounter() const { return itsModifyCounter; } } //# NAMESPACE CASACORE - END #endif casacore-2.4.1/tables/Tables/TableTrace.cc000066400000000000000000000261041321422335000202750ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: BaseColumn.cc 21130 2011-10-18 07:39:05Z gervandiepen $ #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // Define the statics. 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. if (theirDoTrace == 0) { initTracing(); } int tabid = -1; if (theirDoTrace > 0) { ScopedMutexLock 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) { if (theirDoTrace == 0) { initTracing(); } if (theirDoTrace > 0) { ScopedMutexLock 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) { if (theirDoTrace == 0) { initTracing(); } if (theirDoTrace > 0) { writeTraceFirst (tabid, '*'+oper+'*', 't'); *theirStream << endl; } } void TableTrace::traceRefTable (const String& parentName, char oper) { if (theirDoTrace == 0) { initTracing(); } if (theirDoTrace > 1) { int tabid = findTable (parentName); writeTraceFirst (tabid, "*reftable*", oper); *theirStream << endl; } } int TableTrace::traceColumn (const ColumnDesc& cd) { if (theirDoTrace == 0) { 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(); 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 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-2.4.1/tables/Tables/TableVector.h000066400000000000000000000227621321422335000203510ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (uInt 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() (uInt 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 (uInt index, const T& value); // Get nr of dimensions (is always 1). uInt ndim() const; // Get nr of elements (ie. vector length). uInt 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 uInt 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() (uInt 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 (uInt 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-2.4.1/tables/Tables/TableVector.tcc000066400000000000000000000070671321422335000206740ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (uInt 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-2.4.1/tables/Tables/TiledCellStMan.h000066400000000000000000000003601321422335000207310ustar00rootroot00000000000000#ifndef TABLES_TABLES_STUB_TILEDCELLSTMAN_H #define TABLES_TABLES_STUB_TILEDCELLSTMAN_H #include #warning "Tables/TiledCellStMan.h has been deprecated; use DataMan/TiledCellStMan.h instead" #endif casacore-2.4.1/tables/Tables/TiledColumnStMan.h000066400000000000000000000003721321422335000213120ustar00rootroot00000000000000#ifndef TABLES_TABLES_STUB_TILEDCOLUMNSTMAN_H #define TABLES_TABLES_STUB_TILEDCOLUMNSTMAN_H #include #warning "Tables/TiledColumnStMan.h has been deprecated; use DataMan/TiledColumnStMan.h instead" #endif casacore-2.4.1/tables/Tables/TiledDataStMan.h000066400000000000000000000003601321422335000207230ustar00rootroot00000000000000#ifndef TABLES_TABLES_STUB_TILEDDATASTMAN_H #define TABLES_TABLES_STUB_TILEDDATASTMAN_H #include #warning "Tables/TiledDataStMan.h has been deprecated; use DataMan/TiledDataStMan.h instead" #endif casacore-2.4.1/tables/Tables/TiledDataStManAccessor.h000066400000000000000000000004301321422335000224040ustar00rootroot00000000000000#ifndef TABLES_TABLES_STUB_TILEDDATASTMANACCESSOR_H #define TABLES_TABLES_STUB_TILEDDATASTMANACCESSOR_H #include #warning "Tables/TiledDataStManAccessor.h has been deprecated; use DataMan/TiledDataStManAccessor.h instead" #endif casacore-2.4.1/tables/Tables/TiledShapeStMan.h000066400000000000000000000003651321422335000211170ustar00rootroot00000000000000#ifndef TABLES_TABLES_STUB_TILEDSHAPESTMAN_H #define TABLES_TABLES_STUB_TILEDSHAPESTMAN_H #include #warning "Tables/TiledShapeStMan.h has been deprecated; use DataMan/TiledShapeStMan.h instead" #endif casacore-2.4.1/tables/Tables/TiledStManAccessor.h000066400000000000000000000004041321422335000216130ustar00rootroot00000000000000#ifndef TABLES_TABLES_STUB_TILEDSTMANACCESSOR_H #define TABLES_TABLES_STUB_TILEDSTMANACCESSOR_H #include #warning "Tables/TiledStManAccessor.h has been deprecated; use DataMan/TiledStManAccessor.h instead" #endif casacore-2.4.1/tables/Tables/UDFBase.h000066400000000000000000000003071321422335000173370ustar00rootroot00000000000000#ifndef TABLES_TABLES_STUB_UDFBASE_H #define TABLES_TABLES_STUB_UDFBASE_H #include #warning "Tables/UDFBase.h has been deprecated; use TaQL/UDFBase.h instead" #endif casacore-2.4.1/tables/Tables/test/000077500000000000000000000000001321422335000167345ustar00rootroot00000000000000casacore-2.4.1/tables/Tables/test/CMakeLists.txt000066400000000000000000000024051321422335000214750ustar00rootroot00000000000000set (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 tTableVector tTable_1 tTable_2 tTable_3 tTable_4 ) # Some test sources include a test .h file. include_directories ( . ) foreach (test ${tests}) add_executable (${test} ${test}.cc) target_link_libraries (${test} casa_tables) add_test (${test} ${CMAKE_SOURCE_DIR}/cmake/cmake_assay ./${test}) add_dependencies(check ${test}) endforeach (test) casacore-2.4.1/tables/Tables/test/ascii2Table.cc000066400000000000000000000111601321422335000213640ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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 (AipsError x) { cout << "\nCaught an exception: " << x.getMesg() << 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 using namespace casacore; uInt nRows=7; uInt nChannels=5; uInt nCorrelations=3; Array referenceArray (IPosition (3, nCorrelations, nChannels, nRows)); // Create the table. void createTable ( const Array array) { 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), ColumnDesc::FixedShape)); // Now create a new table from the description. SetupNewTable newtab("tArrayColumnCellSlices_tmp.data", td, Table::New); 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 (AipsError & e){ } } { // Check ColumnSlicer validation logic II // // Slicer lists must be same length Vector dataSlicer (3, 0); Vector destinationSlicer (2, 0); IPosition shape (1, 0); try { ColumnSlicer columnSlicer (shape, dataSlicer, destinationSlicer); AlwaysAssertExit (False); // shouldn't get here } catch (AipsError & e){ } } { // Check ColumnSlicer validation logic III // // Slicer elements must have same length Vector dataSlicer (1, 0); Vector destinationSlicer (1, 0); 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 (AipsError & e){ } } { // Check the actual I/O now // // Read the whole thing at once. Vector dataSlicer (1, 0); Vector destinationSlicer (1, 0); 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, 0); Vector destinationSlicer (2, 0); 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, 0); Vector destinationSlicer (2, 0); 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, 0); Vector destinationSlicer (2, 0); 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, 0); Vector destinationSlicer (2, 0); 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, 0); Vector sourceSlicer (1, 0); 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, 0); Vector sourceSlicer (2, 0); 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, 0); Vector sourceSlicer (2, 0); 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, 0); Vector sourceSlicer (2, 0); 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, 0); Vector sourceSlicer (2, 0); 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); createTable (referenceArray); readCellSlices(); writeCellSlices(); } catch (AipsError& x) { cout << "Caught an exception: " << x.getMesg() << endl; return 1; } return 0; // exit with success status } casacore-2.4.1/tables/Tables/test/tArrayColumnSlices.cc000066400000000000000000000151241321422335000230310ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError& x) { cout << "Caught an exception: " << x.getMesg() << endl; return 1; } return 0; // exit with success status } casacore-2.4.1/tables/Tables/test/tColumnsIndex.cc000066400000000000000000000277011321422335000220460ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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; Vector rows; Bool found; char str[8]; // Test each individual type. uInt i; for (i=0; i& fieldPtrs, const Block& dataPtrs, const Block& dataTypes, Int 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 (AipsError x) { cout << "Exception caught: " << x.getMesg() << endl; return 1; } return 0; } casacore-2.4.1/tables/Tables/test/tColumnsIndex.out000066400000000000000000000004471321422335000222660ustar00rootroot00000000000000ColumnsIndex::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-2.4.1/tables/Tables/test/tColumnsIndexArray.cc000066400000000000000000000143711321422335000230440ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { cout << x.getMesg() << 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 (AipsError x) { cout << "Exception caught: " << x.getMesg() << endl; return 1; } return 0; } casacore-2.4.1/tables/Tables/test/tColumnsIndexArray.out000066400000000000000000000005011321422335000232540ustar00rootroot00000000000000[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-2.4.1/tables/Tables/test/tConcatRows.cc000066400000000000000000000155341321422335000215210ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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; uInt 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 (AipsError& 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 (AipsError& x) { cout << "\nCaught an exception: " << x.getMesg() << endl; return 1; } return 0; // successfully executed } casacore-2.4.1/tables/Tables/test/tConcatTable.cc000066400000000000000000000235001321422335000216060ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 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); sprintf (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; // Add a column. Table tabrw(tab); tabrw.reopenRW(); tabrw.addColumn (ScalarColumnDesc("newcol")); AlwaysAssertExit (tab.tableDesc().isColumn ("newcol")); } void doIt1 (const String& tableName) { cout << ">>> -------------------" << endl; cout << "start reading Tables: " << tableName << endl; cout << "<<<" << endl; Table tab(tableName); cout << "end reading Tables" << endl; uInt 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-2.4.1/tables/Tables/test/tConcatTable.run000077500000000000000000000002511321422335000220260ustar00rootroot00000000000000#!/bin/sh # # $Id$ # 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-2.4.1/tables/Tables/test/tConcatTable2.cc000066400000000000000000000222641321422335000216760ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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[8]; 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)); sprintf (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 (AipsError x) { cout << "Exception caught: " << x.getMesg() << endl; return 1; } return 0; } casacore-2.4.1/tables/Tables/test/tConcatTable3.cc000066400000000000000000000074351321422335000217020ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { cout << "Exception caught: " << x.getMesg() << endl; return 1; } return 0; } casacore-2.4.1/tables/Tables/test/tIncrementalStMan2.cc000066400000000000000000000112531321422335000227170ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: tIncrementalStMan.cc 21451 2014-06-10 07:48:08Z gervandiepen $ #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 (AipsError& x) { cout << "Caught an exception: " << x.getMesg() << endl; return 1; } return 0; // exit with success status } casacore-2.4.1/tables/Tables/test/tMemoryTable.cc000066400000000000000000000424641321422335000216610ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { cout << "Caught an exception: " << x.getMesg() << 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-2.4.1/tables/Tables/test/tMemoryTable.out000066400000000000000000001465221321422335000221030ustar00rootroot00000000000000after 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-2.4.1/tables/Tables/test/tReadAsciiTable.cc000066400000000000000000000504231321422335000222270ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { cout << "Caught an exception: " << x.getMesg() << 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-2.4.1/tables/Tables/test/tReadAsciiTable2.cc000066400000000000000000000031741321422335000223120ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/tables/Tables/test/tReadAsciiTable2.in000066400000000000000000000001611321422335000223240ustar00rootroot0000000000000012. 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-2.4.1/tables/Tables/test/tReadAsciiTable2.out000066400000000000000000000001301321422335000225210ustar00rootroot0000000000000012 3.14159 3.14159 -3.14159 0.20944 3.14159 12 0.20944 0.20944 -0.20944 0.20944 0.20944 casacore-2.4.1/tables/Tables/test/tRefRows.cc000066400000000000000000000076211321422335000210240ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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, uInt(1)); rows(17) = 0; RefRows ref(rows); AlwaysAssertExit (ref.nrows() == 18); AlwaysAssertExit (!ref.isSliced()); cout << ref.convert(vec) << endl; } { Vector rows(18); indgen (rows, uInt(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 (AipsError x) { cout << "\nCaught an exception: " << x.getMesg() << endl; return 1; } return 0; // successfully executed } casacore-2.4.1/tables/Tables/test/tRefRows.out000066400000000000000000000006261321422335000212440ustar00rootroot00000000000000unSliced,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-2.4.1/tables/Tables/test/tRefTable.cc000066400000000000000000000103421321422335000211130ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/tables/Tables/test/tRowCopier.out000066400000000000000000000014501321422335000215620ustar00rootroot00000000000000Building 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-2.4.1/tables/Tables/test/tScalarRecordColumn.cc000066400000000000000000000147011321422335000231540ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { cout << "Caught an exception: " << x.getMesg() << endl; return 1; } return 0; // exit with success status } casacore-2.4.1/tables/Tables/test/tScalarRecordColumn.out000066400000000000000000000004631321422335000233760ustar00rootroot00000000000000 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-2.4.1/tables/Tables/test/tTable.cc000066400000000000000000000750021321422335000204620ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include #include #include #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 (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); 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 (AipsError& x) { // not FixedShape cout << "Expected exception: "<< x.getMesg() << 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 (AipsError& x) { // value too long cout << "Expected exception: " << x.getMesg() << endl; } try { arr1.put (0, vec2); } catch (AipsError& x) { // shape cannot change cout << "Expected exception: " << x.getMesg() << endl; } } } void b (Bool doExcp) { // Get the description and #rows of the Table. cout << "get layout in static way" << endl; TableDesc layout; cout << "Table::getlayout #rows = " << Table::getLayout (layout, "tTable_tmp.data"); layout.show (cout); cout << endl; TableInfo info(Table::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 (AipsError& x) { // table not writable cout << "Expected exception: " << removeDir(x.getMesg()) << 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); sprintf (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 (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); // 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 (AipsError& x) { // exists as file cout << "Expected exception: " << removeDir(x.getMesg()) << endl; } try { tab.rename ("tTable_tmp.data", Table::NewNoReplace); } catch (AipsError& x) { // already exists cout << "Expected exception: " << removeDir(x.getMesg()) << endl; } try { tab.rename ("tTable.datx", Table::Update); } catch (AipsError& x) { // does not exist cout << "Expected exception: " << removeDir(x.getMesg()) << endl; } try { tab.addColumn (ScalarColumnDesc("ab")); } catch (AipsError& x) { // column already exists cout << "Expected exception: " << x.getMesg() << 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); sprintf (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 (AipsError& x) { // row does not exist cout << "Expected exception: " << removeDir(x.getMesg()) << 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() { 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); // 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); uInt i; uInt nrow = tab.nrow(); for (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); a ( (argc<2)); b ( (argc<2)); c ( (argc<2)); d (); } catch (AipsError& x) { cout << "Caught an exception: " << x.getMesg() << endl; return 1; } return 0; // exit with success status } casacore-2.4.1/tables/Tables/test/tTable.out000066400000000000000000000143631321422335000207070ustar00rootroot00000000000000Expected 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 get layout in static way Table::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-2.4.1/tables/Tables/test/tTableAccess.cc000066400000000000000000000122761321422335000216100ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError& x) { cout << "Caught an exception: " << x.getMesg() << endl; return 1; } return 0; // exit with success status } casacore-2.4.1/tables/Tables/test/tTableCopy.cc000066400000000000000000000157211321422335000213170ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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; } // 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 = TableCopy::removeDminfoColumns (dminfo, Vector(1, "col1"), "Standard"); cout << dminfo << remCols1 << endl; Vector remCols2 = TableCopy::removeDminfoColumns (dminfo, Vector(1, "col1")); cout << dminfo << remCols2 << endl; TableCopy::setTiledStMan (dminfo, Vector(1, "col3"), "TiledShapeStMan", "TSMData", IPosition(3,3,4,5)); cout << dminfo << endl; } 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) { testDM(); testCloneColumns(); } } catch (const exception& x) { cout << x.what() << endl; return 1; } return 0; } casacore-2.4.1/tables/Tables/test/tTableCopy.out000066400000000000000000000325601321422335000215410ustar00rootroot00000000000000tTableCopy_tmp.tbl tTableCopy_tmp.tbl/SUBTABLE tTableCopy_tmp.newtbl tTableCopy_tmp.newtbl/SUBTABLE 1 1 1 1 0 0 *1: { TYPE: String "StandardStMan" NAME: String "StandardStMan" SEQNR: uInt 0 SPEC: { ActualCacheSize: 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: { ActualCacheSize: 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: { ActualCacheSize: 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: { ActualCacheSize: 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] } } testCloneColumn ... *1: { TYPE: String "StandardStMan" NAME: String "SSM" SEQNR: uInt 0 SPEC: { ActualCacheSize: 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: { ActualMaxCacheSize: Int 0 DEFAULTTILESHAPE: Int array with shape [2] [8, 2] MAXIMUMCACHESIZE: Int 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: { ActualMaxCacheSize: Int 0 DEFAULTTILESHAPE: Int array with shape [2] [8, 2] MAXIMUMCACHESIZE: Int 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: { ActualMaxCacheSize: Int 0 DEFAULTTILESHAPE: Int array with shape [2] [8, 2] MAXIMUMCACHESIZE: Int 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: { ActualMaxCacheSize: Int 0 DEFAULTTILESHAPE: Int array with shape [2] [8, 2] MAXIMUMCACHESIZE: Int 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: { ActualCacheSize: 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: { ActualCacheSize: 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: { ActualMaxCacheSize: Int 0 DEFAULTTILESHAPE: Int array with shape [2] [8, 2] MAXIMUMCACHESIZE: Int 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: { ActualMaxCacheSize: Int 0 DEFAULTTILESHAPE: Int array with shape [2] [8, 2] MAXIMUMCACHESIZE: Int 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: { ActualMaxCacheSize: Int 0 DEFAULTTILESHAPE: Int array with shape [2] [8, 2] MAXIMUMCACHESIZE: Int 0 HYPERCUBES: { } SEQNR: uInt 3 } COLUMNS: String array with shape [1] [DATA2] } *5: { TYPE: String "TiledCellStMan" NAME: String "DATA3" SEQNR: uInt 4 SPEC: { ActualMaxCacheSize: Int 0 DEFAULTTILESHAPE: Int array with shape [2] [8, 2] MAXIMUMCACHESIZE: Int 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: { ActualCacheSize: 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: { ActualCacheSize: 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: { ActualMaxCacheSize: Int 0 DEFAULTTILESHAPE: Int array with shape [2] [8, 2] MAXIMUMCACHESIZE: Int 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: { ActualMaxCacheSize: Int 0 DEFAULTTILESHAPE: Int array with shape [2] [8, 2] MAXIMUMCACHESIZE: Int 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: { ActualMaxCacheSize: Int 0 DEFAULTTILESHAPE: Int array with shape [2] [8, 2] MAXIMUMCACHESIZE: Int 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: { ActualMaxCacheSize: Int 0 DEFAULTTILESHAPE: Int array with shape [2] [8, 2] MAXIMUMCACHESIZE: Int 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: { ActualCacheSize: 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-2.4.1/tables/Tables/test/tTableCopy.run000077500000000000000000000005251321422335000215350ustar00rootroot00000000000000#!/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-2.4.1/tables/Tables/test/tTableCopyPerf.cc000066400000000000000000000054321321422335000221320ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/tables/Tables/test/tTableDesc.cc000066400000000000000000000367371321422335000212750ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { cout << x.getMesg() << 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 (AipsError x) { cout << x.getMesg() << 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 (AipsError x) { cout << x.getMesg() << endl; // column is a scalar } try { td.rwColumnDesc("ab").setShape (IPosition()); } catch (AipsError x) { cout << x.getMesg() << endl; // column is a scalar } try { td.rwColumnDesc("Arr3").setNdim (2); } catch (AipsError x) { cout << x.getMesg() << endl; // ndim already defined } try { td.rwColumnDesc("Arr3").setShape (IPosition()); } catch (AipsError x) { cout << x.getMesg() << 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 (AipsError x) { cout << x.getMesg() << 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 (AipsError x) { cout << x.getMesg() << endl; // shape mimatches ndim } } td.rwColumnDesc("Arr1").setShape (IPosition(2,4,5)); if (doExcp) { try { td["Arr1"].tableDesc(); // no subtable } catch (AipsError x) { cout << x.getMesg() << 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 (AipsError x) { cout << x.getMesg() << endl; } try { td.renameColumn ("colintnew", "colintxxx"); } catch (AipsError x) { cout << x.getMesg() << 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) {} ExampleDesc(const ExampleDesc& that): x_p(that.x_p), y_p(that.y_p) {} 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 (AipsError x) { cout << "Caught an exception: " << x.getMesg() << 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 (AipsError x) { cout << x.getMesg() << endl; // no data columns } try { td.defineHypercolumn ("TSMExample", 0, stringToVector ("Data,Weight")); } catch (AipsError x) { cout << x.getMesg() << endl; // ndim < 1 } try { td.defineHypercolumn ("TSMExample", 4, stringToVector ("Data,Weight"), stringToVector ("Pol,Freq,Baseline")); } catch (AipsError x) { cout << x.getMesg() << endl; // ndim != #coord } try { td.defineHypercolumn ("TSMExample", 4, stringToVector ("Data,Weight"), stringToVector ("Pol,Freq,Baseline,Timex")); } catch (AipsError x) { cout << x.getMesg() << endl; // Timex does not exist } try { td.defineHypercolumn ("TSMExample", 4, stringToVector ("Data,Weight,Datax")); } catch (AipsError x) { cout << x.getMesg() << endl; // Datax does not exist } try { td.defineHypercolumn ("TSMExample", 4, stringToVector ("Data,Weight"), stringToVector ("Pol,Freq,Baseline,Time"), stringToVector ("Idx")); } catch (AipsError x) { cout << x.getMesg() << endl; // Idx does not exist } try { td.defineHypercolumn ("TSMExample", 4, stringToVector ("Data,Weight"), stringToVector ("Pol,Freq,Baseline,TimeNotNum")); } catch (AipsError x) { cout << x.getMesg() << endl; // TimeNotNum not numeric } try { td.defineHypercolumn ("TSMExample", 4, stringToVector ("Data,Weight"), stringToVector ("Pol,Freq,Baseline,TimeShort")); } catch (AipsError x) { cout << x.getMesg() << endl; // Coord short not supported } try { td.defineHypercolumn ("TSMExample", 4, stringToVector ("Data,Weight"), stringToVector ("Pol,Freq,Baseline,Data")); } catch (AipsError x) { cout << x.getMesg() << endl; // Coord Data is array > 1-dim } try { td.defineHypercolumn ("TSMExample", 4, stringToVector ("Data,Weight"), stringToVector ("Pol,Freq,Baseline,Pol")); } catch (AipsError x) { cout << x.getMesg() << endl; // coord vectors not at start } try { td.defineHypercolumn ("TSMExample", 4, stringToVector ("Data0,Weight")); } catch (AipsError x) { cout << x.getMesg() << endl; // Data #dim undefined } try { td.defineHypercolumn ("TSMExample", 4, stringToVector ("Data1,Weight")); } catch (AipsError x) { cout << x.getMesg() << endl; // Data #dim != Weight #ndim } try { td.defineHypercolumn ("TSMExample", 4, stringToVector ("Data1"), stringToVector ("Pol,Freq,Baseline,Time")); } catch (AipsError x) { cout << x.getMesg() << endl; // Data #dim != #coordVector } try { td.defineHypercolumn ("TSMExample", 4, stringToVector ("Data"), stringToVector ("Pol,Time,Baseline,Time")); } catch (AipsError x) { cout << x.getMesg() << endl; // Data #dim != #coordVector } try { td.defineHypercolumn ("TSMExample", 4, stringToVector ("Data"), stringToVector (",,Pol,")); } catch (AipsError x) { cout << x.getMesg() << endl; // Data #dim != #coordVector } try { td.defineHypercolumn ("TSMExample", 4, stringToVector ("Data,Weight"), stringToVector ("Pol,Freq,Baseline,Time"), stringToVector ("TimeShort")); } catch (AipsError x) { cout << x.getMesg() << endl; // Id short not supported } try { td.defineHypercolumn ("TSMExample", 4, stringToVector ("Data,Weight"), stringToVector ("Pol,Freq,Baseline,Time"), stringToVector ("Data")); } catch (AipsError x) { cout << x.getMesg() << endl; // Id array not supported } try { td.defineHypercolumn ("TSMExample", 4, stringToVector ("Data,Weight"), stringToVector ("Pol,Freq,Baseline,Time"), stringToVector ("Time")); } catch (AipsError x) { cout << x.getMesg() << endl; // Time double used } } casacore-2.4.1/tables/Tables/test/tTableDescHyper.out000066400000000000000000000100731321422335000225100ustar00rootroot00000000000000011110 [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 and (u)Short 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 and (u)Short 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-2.4.1/tables/Tables/test/tTableInfo.cc000066400000000000000000000045121321422335000212740ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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 (AipsError x) { cout << "Caught an exception: " << x.getMesg() << endl; return 1; } return 0; // exit with success status } casacore-2.4.1/tables/Tables/test/tTableInfo.out000066400000000000000000000001041321422335000215070ustar00rootroot00000000000000infotype infosubtype first line second line third line fourth line casacore-2.4.1/tables/Tables/test/tTableIter.cc000066400000000000000000000173611321422335000213120ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 doiter1(); void doiter2(); void doiter3(); 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) doiter1(); // do single column iteration doiter2(); // do two column iteration doiter3(); // do interval iteration 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(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); CompareIntervalReal xx(10., 0.); CountedPtr xxx (new CompareIntervalReal(10., 0.)); compObj[0] = xxx; 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; } casacore-2.4.1/tables/Tables/test/tTableIter.out000066400000000000000000000003621321422335000215250ustar00rootroot00000000000000Filling done 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-2.4.1/tables/Tables/test/tTableKeywords.cc000066400000000000000000000143311321422335000222100ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ //# 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 (AipsError 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); } { // Open the table using the :: syntax. Table subtab3 (Table::openTable("main3data::SubTab2")); AlwaysAssertExit (subtab3.nrow() == 3); } } 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 (AipsError x) { cout << "Caught an exception : " << x.getMesg() << endl; return 1; } cout << "OK" << endl; return 0; } casacore-2.4.1/tables/Tables/test/tTableKeywords.out000066400000000000000000000000031321422335000224210ustar00rootroot00000000000000OK casacore-2.4.1/tables/Tables/test/tTableLock.cc000066400000000000000000000122441321422335000212720ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include #include #include // // Test program for the TableLock class. // #ifdef AIPS_TABLE_NOLOCKING void checkLockOption (const TableLock& lock, TableLock::LockOption, Bool, Bool) { AlwaysAssertExit (lock.option() == TableLock::NoLocking); AlwaysAssertExit (! lock.readLocking()); AlwaysAssertExit (! lock.isPermanent()); } #else void checkLockOption (const TableLock& lock, TableLock::LockOption opt, Bool readLock, Bool permLock) { AlwaysAssertExit (lock.option() == opt); AlwaysAssertExit (lock.readLocking() == readLock); AlwaysAssertExit (lock.isPermanent() == permLock); } #endif 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-2.4.1/tables/Tables/test/tTableLockSync.cc000066400000000000000000000226431321422335000221330ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError 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 (AipsError x) { cout << x.getMesg() << 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 (AipsError x) { cout << x.getMesg() << 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 (AipsError x) { cout << "Caught an exception: " << x.getMesg() << endl; return 1; } } return 0; // exit with success status } casacore-2.4.1/tables/Tables/test/tTableLockSync_2.cc000066400000000000000000000262531321422335000223550ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { cout << "Caught an exception: " << x.getMesg() << endl; return 1; } return 0; // exit with success status } casacore-2.4.1/tables/Tables/test/tTableRecord.cc000066400000000000000000001070071321422335000216220ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { cout << x.getMesg() << endl; // incorrect shape } try { *rfstr3 = stringToVector ("abc"); } catch (AipsError x) { cout << x.getMesg() << 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 (AipsError x) { cout << x.getMesg() << endl; // incorrect shape } try { *rfstr3 = stringToVector ("abc"); } catch (AipsError x) { cout << x.getMesg() << 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 (AipsError x) { cout << x.getMesg() << 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 (AipsError x) { cout << x.getMesg() << 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 (AipsError x) { cout << ">>> Instance-specific assertion error message:" << endl; cout << x.getMesg() << 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 (AipsError x) { cout << x.getMesg() << endl; // empty name } try { record.define ("aB", (Int)0); } catch (AipsError x) { cout << x.getMesg() << endl; // first no uppercase } extraArgument = 10; try { record.define ("A", (Int)0); } catch (AipsError x) { cout << x.getMesg() << endl; // extra argument = 10 } extraArgument = 0; try { record.define ("TpShort", (Int)0); } catch (AipsError x) { cout << x.getMesg() << 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 AlwaysAssertExit (! fld2.isAttached()); // restructure and operator= fail on a non-empty, fixed record. TableRecord record2a(RecordInterface::Fixed); if (doExcp) { try { record2a.restructure(subDesc); } catch (AipsError x) { cout << x.getMesg() << endl; // fixed, not empty ->impossible } } record2 = record; // non-fixed -> possible record2a = record; // same structure -> possible TableRecord record2b (subDesc); if (doExcp) { try { record2a = record2b; } catch (AipsError x) { cout << ">>> Instance-specific assertion error message:" << endl; cout << x.getMesg() << 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 (AipsError x) { cout << x.getMesg() << 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 (! tpstring3.isAttached()); AlwaysAssertExit (tpstring4.isAttached()); AlwaysAssertExit (tpstring2.fieldNumber() == 33); AlwaysAssertExit (tpstring3.fieldNumber() == -1); AlwaysAssertExit (tpstring4.fieldNumber() == 34); 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 rf1(*record6, 0); RecordFieldPtr rf2(*record6, 0); // virtual void notify(const Notice &message); // implicit delete record6; AlwaysAssertExit(! rf1.isAttached()); AlwaysAssertExit(! rf2.isAttached()); // 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 (AipsError x) { cout << x.getMesg() << endl; // non-conforming } try { RecordFieldPtr

      • fld1 (rec1, "tab1"); } catch (AipsError x) { cout << x.getMesg() << 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 (AipsError x) { cout << "Caught an exception: " << x.getMesg() << endl; return 1; } cout << "OK" << endl; return 0; // exit with success status } casacore-2.4.1/tables/Tables/test/tTableRecord.out000066400000000000000000000070631321422335000220450ustar00rootroot00000000000000Record 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-2.4.1/tables/Tables/test/tTableRow.cc000066400000000000000000000300641321422335000211510ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { cout << x.getMesg() << endl; // not writable } try { ROTableRow row (tab, stringToVector("ab,abb")); } catch (AipsError x) { cout << x.getMesg() << 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 (AipsError x) { cout << "Caught an exception: " << x.getMesg() << endl; return 1; } cout << "OK" << endl; return 0; // exit with success status } casacore-2.4.1/tables/Tables/test/tTableRow.out000066400000000000000000000011121321422335000213630ustar00rootroot00000000000000TableRow 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-2.4.1/tables/Tables/test/tTableTrace.cc000066400000000000000000000073351321422335000214450ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (uInt 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 (uInt 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))); } { Table rtab (tab(abv)); ScalarColumn ab1(rtab,"ab"); ArrayColumn ad(rtab,"ad"); Vector abv = ab1.getColumn(); Array adv = ad.getColumn(); } } int main() { try { uInt nrrow = 5; testTable (nrrow); } catch (const AipsError& x) { cout << "Caught an exception: " << x.getMesg() << endl; return 1; } return 0; // exit with success status } casacore-2.4.1/tables/Tables/test/tTableTrace.out000066400000000000000000000023101321422335000216530ustar00rootroot00000000000000# 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 0 [8] time2 r t=0 ad 1 [8] time2 r t=0 ad 2 [8] time2 r t=0 ad 3 [8] time2 r t=0 ad 4 [8] 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 time2 n t=0 tTableTrace_tmp.tab time2 w t=0 ab 0 time2 w t=0 ab 1 time2 w t=0 ab 2 time2 w t=0 ab 3 time2 w t=0 ab 4 time2 t t=0 *flush* time2 c t=0 tTableTrace_tmp.tab time2 o t=0 tTableTrace_tmp.tab time2 r t=0 ab * time2 r t=0 ab 0:3 time2 s t=0 *reftable* time2 r t=0 ab 0,1,2,3,4 time2 c t=0 *reftable* time2 c t=0 tTableTrace_tmp.tab casacore-2.4.1/tables/Tables/test/tTableTrace.run000077500000000000000000000014071321422335000216610ustar00rootroot00000000000000#!/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 #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-2.4.1/tables/Tables/test/tTable_1.cc000066400000000000000000000066551321422335000207120ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { cout << "Caught an exception: " << x.getMesg() << 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 (AipsError x) { cout << x.getMesg() << endl; } /// try { /// Table tab2(newtab, 10); // newtab is already in use /// } catch (AipsError x) { /// cout << x.getMesg() << endl; /// } } casacore-2.4.1/tables/Tables/test/tTable_1.out000066400000000000000000000001211321422335000211120ustar00rootroot00000000000000Invalid Table operation: SetupNewTable::bindColumn, object already used by Table casacore-2.4.1/tables/Tables/test/tTable_2.cc000066400000000000000000000221641321422335000207040ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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); sprintf (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-2.4.1/tables/Tables/test/tTable_2.data_v0/000077500000000000000000000000001321422335000217055ustar00rootroot00000000000000casacore-2.4.1/tables/Tables/test/tTable_2.data_v0/table.dat000066400000000000000000000050261321422335000234710ustar00rootroot00000000000000¾¾¾¾ 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-2.4.1/tables/Tables/test/tTable_2.data_v0/table.f0i0000066400000000000000000000021601321422335000234530ustar00rootroot00000000000000p?€@@@@€@ @À@à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-2.4.1/tables/Tables/test/tTable_2.data_v0/table.f1000066400000000000000000000002041321422335000232200ustar00rootroot00000000000000¾¾¾¾€ StManAipsIO QStManColumnAipsIO  casacore-2.4.1/tables/Tables/test/tTable_2.data_v0/table.f2000066400000000000000000000003551321422335000232300ustar00rootroot00000000000000¾¾¾¾é StManAipsIO  QStManColumnAipsIO @@@€@ @À@àAAA A0A@eStManColumnAipsIO V0V1V2V3V4V5V6V7V8V9casacore-2.4.1/tables/Tables/test/tTable_2.data_v0/table.info000066400000000000000000000001211321422335000236430ustar00rootroot00000000000000Type = testtype SubType = testsubtype first readme line second test readme line casacore-2.4.1/tables/Tables/test/tTable_2.data_v1/000077500000000000000000000000001321422335000217065ustar00rootroot00000000000000casacore-2.4.1/tables/Tables/test/tTable_2.data_v1/table.dat000066400000000000000000000040041321422335000234650ustar00rootroot00000000000000¾¾¾¾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-2.4.1/tables/Tables/test/tTable_2.data_v1/table.f0i0000066400000000000000000000021601321422335000234540ustar00rootroot00000000000000p?€@@@@€@ @À@à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-2.4.1/tables/Tables/test/tTable_2.data_v1/table.f1000066400000000000000000000002041321422335000232210ustar00rootroot00000000000000¾¾¾¾€ StManAipsIO QStManColumnAipsIO  casacore-2.4.1/tables/Tables/test/tTable_2.data_v1/table.f2000066400000000000000000000003551321422335000232310ustar00rootroot00000000000000¾¾¾¾é StManAipsIO  QStManColumnAipsIO @@@€@ @À@àAAA A0A@eStManColumnAipsIO V0V1V2V3V4V5V6V7V8V9casacore-2.4.1/tables/Tables/test/tTable_2.data_v1/table.info000066400000000000000000000001211321422335000236440ustar00rootroot00000000000000Type = testtype SubType = testsubtype first readme line second test readme line casacore-2.4.1/tables/Tables/test/tTable_2.out000066400000000000000000000032321321422335000211210ustar00rootroot00000000000000>>> ------------------- 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-2.4.1/tables/Tables/test/tTable_2.run000066400000000000000000000011531321422335000211160ustar00rootroot00000000000000#!/bin/sh #----------------------------------------------------------------------------- # Usage: tTable_2.exec #----------------------------------------------------------------------------- # 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. # # $Id$ #----------------------------------------------------------------------------- # Fundamentals. TABLES="$testsrcdir/tTable_2.data_v*" $casa_checktool ./tTable_2 $TABLES casacore-2.4.1/tables/Tables/test/tTable_3.cc000066400000000000000000000127311321422335000207040ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 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 (AipsError x) { cout << "Caught an exception: " << x.getMesg() << endl; return 1; } return 0; // exit with success status } casacore-2.4.1/tables/Tables/test/tTable_4.cc000066400000000000000000000240061321422335000207030ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 (AipsError x) { cout << x.getMesg() << endl; } } // Create the hypercolumn descriptions for all tiled columns. SimpleOrderedMap map(""); for (uInt i=0; i vec = stringToVector(cols.from(1)); uInt ndim = 2; if (td.columnDesc(vec(0)).isScalar()) { ndim = 1; } td.defineHypercolumn (map.getKey(i), 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; stringToVector(str).toBlock (cols); 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 using namespace casacore; using namespace std; void show (MultiFileBase& 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; i::const_iterator iter=fname.begin(); iter!=fname.end(); ++iter) { if (iter->empty()) { cerr << "*** Empty file name given" << endl; } else { if (HDF5File::isHDF5(*iter)) { MultiHDF5 mfile (*iter, ByteIO::Old); show (mfile, showbl, "MultiHDF5"); } else { MultiFile mfile (*iter, ByteIO::Old); show (mfile, showbl, "MultiFile"); } } } cout << endl; } catch (const std::exception& x) { cerr << x.what() << endl; return 1; } return 0; } casacore-2.4.1/tables/apps/showtableinfo.cc000066400000000000000000000125541321422335000206700ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #include #include #include #include #include #include #include #include #include // for mkstemp #include // for strerror #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); } // 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 using namespace casacore; using namespace std; int main (int argc, char* argv[]) { if (TableLock::lockingDisabled()) { cerr << "Note: table locking is disabled because Casacore " << "was built with -DAIPS_TABLES_NOLOCKING" << endl; } if (argc < 2) { cerr << "Use as: showtablelock tablename" << endl; return 1; } try { String tablename(argv[1]); tablename = Path(tablename).absoluteName(); if (! Table::isReadable (tablename)) { cerr << "Table " << tablename << " does not exist (or not readable)" << endl; return 1; } uInt pid = 0; Bool permLocked = False; uInt type = LockFile::showLock (pid, permLocked, tablename + "/table.lock"); 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; } } catch (std::exception& x) { cerr << x.what() << endl; return 1; } return 0; } casacore-2.4.1/tables/apps/tablefromascii.cc000066400000000000000000000110421321422335000207770ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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-2.4.1/tables/apps/taql.cc000066400000000000000000001034721321422335000167650ustar00rootroot00000000000000//# 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #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 table commands from user interface // // Define the type for the map of name to (resulttable,command). typedef map > TableMap; 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& times, const String& unit) { 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) { cout << ", "; } else { firstTime = False; } q.setValue (*iter); showTime (q); } cout << ']'; } void showPos (const Array& pos, const Vector& units) { 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()) { cout << '['; } Array::const_iterator endIter = pos.end(); for (Array::const_iterator iter= pos.begin(); iter != endIter;) { if (!firstTime) { cout << ", "; } else { firstTime = False; } for (uInt i=0; i& dir, const Vector& units) { 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()) { cout << '['; } Array::const_iterator endIter = dir.end(); for (Array::const_iterator iter= dir.begin(); iter != endIter;) { if (!firstTime) { cout << ", "; } else { firstTime = False; } cout << '['; for (uInt i=0; i void showArray (const Array& arr) { if (arr.size() == 1) { cout << arr.data()[0]; } else { cout << arr; } } template<> void showArray (const Array& arr) { if (arr.size() == 1) { showTime (arr.data()[0]); } else { Bool firstTime = True; cout << '['; Array::const_iterator endIter = arr.end(); for (Array::const_iterator iter= arr.begin(); iter != endIter; ++iter) { if (!firstTime) { cout << ", "; } else { firstTime = False; } showTime (*iter); } cout << ']'; } } // Show the required columns. // First test if they exist and contain scalars or arrays. void showTable (const Table& tab, const Vector& colnam, bool printMeas, const String& delim) { 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; uInt i; for (i=0; icolumnDesc().isScalar() && ! tableColumns[nrcol]->columnDesc().isArray()) { cout << "Column " << colnam(i) << " contains 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 (printMeas) { 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) { cout << "Unit: "; for (uInt j=0; j 0) { cout << delim; } cout << colUnits[j]; } cout << endl; } // Use TableProxy, so we can be type-agnostic. TableProxy proxy(tab); for (i=0; i 0) { cout << delim; } if (! tableColumns[j]->isDefined (i)) { cout << " 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]); } else { showTime (vh.asArrayDouble(), timeUnit[j][0]); } } else if (! posUnit[j].empty()) { showPos (vh.asArrayDouble(), posUnit[j]); } else if (! dirUnit[j].empty()) { showDir (vh.asArrayDouble(), dirUnit[j]); } else if (vh.dataType() == TpBool) { // std::boolalpha seems to persist. cout << (vh.asBool() ? "true" : "false"); } else { cout << vh; } } } cout << endl; } for (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. cout << "Index: " << indices.start() << endl; } } const Unit& unit = expr.unit(); if (! unit.empty()) { cout << "Unit: " << unit.getName() << endl; } Vector rownrs (expr.nrow()); indgen (rownrs); if (expr.isScalar()) { switch (expr.getColumnDataType()) { case TpBool: showArray (expr.getColumnBool (rownrs)); break; case TpUChar: showArray (expr.getColumnuChar (rownrs)); break; case TpShort: showArray (expr.getColumnShort (rownrs)); break; case TpUShort: showArray (expr.getColumnuShort (rownrs)); break; case TpInt: showArray (expr.getColumnInt (rownrs)); break; case TpUInt: showArray (expr.getColumnuInt (rownrs)); break; case TpFloat: showArray (expr.getColumnFloat (rownrs)); break; case TpDouble: showArray (expr.getColumnDouble (rownrs)); break; case TpComplex: showArray (expr.getColumnComplex (rownrs)); break; case TpDComplex: showArray (expr.getColumnDComplex (rownrs)); break; case TpString: showArray (expr.getColumnString (rownrs)); break; case TpQuantity: { AlwaysAssert (expr.getNodeRep()->dataType() == TableExprNodeRep::NTDate, AipsError); MVTime time; if (expr.nrow() != 1) cout << '['; for (uInt i=0; i 0) cout << ", "; expr.get (i, time); showTime (time); } if (expr.nrow() != 1) cout << ']'; } break; default: cout << "Unknown expression scalar type " << expr.getColumnDataType(); } cout << endl; } else { for (uInt i=0; i 1) { cout << " row " << i << ": "; } switch (expr.dataType()) { case TpBool: showArray (expr.getArrayBool(i)); break; case TpInt: showArray (expr.getArrayInt(i)); break; case TpDouble: showArray (expr.getArrayDouble(i)); break; case TpDComplex: showArray (expr.getArrayDComplex(i)); break; case TpString: showArray (expr.getArrayString(i)); break; case TpQuantity: { AlwaysAssert (expr.getNodeRep()->dataType() == TableExprNodeRep::NTDate, AipsError); showArray (expr.getArrayDate(i)); } break; default: cout << "Unknown expression array type " << expr.dataType(); } cout << endl; } } } void showParseError (const TableParseError& x) { // Try to highlight 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.getMesg()); 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. String::size_type spos = str.find_first_not_of (' '); Bool addComm = False; Bool showHelp = False; Bool doCount = False; Bool showResult = False; if (spos != String::npos) { String::size_type epos = str.find (' ', spos); if (epos == String::npos) { epos = str.size(); } String s = str.substr(spos, epos-spos); s.downcase(); showHelp = (s=="show" || s=="help"); addComm = !(s=="select" || s=="update" || s=="insert" || s=="calc" || s=="delete" || s=="count" || s=="create" || s=="createtable" || s=="alter" || s=="altertable" || s=="using" || s=="usingstyle" || s=="time" || showHelp); showResult = (s=="select"); if (s=="count") { doCount = True; showResult = True; } } String strc(str); if (addComm) { strc = "SELECT " + str; printCommand = False; printRows = False; showResult = True; printHeader = False; } strc = prefix + strc; Table tabp; uInt i; Vector colNames; String cmd; TaQLResult result; result = tableCommand (strc, tempTables, colNames, cmd); // Show result of COUNT as well. if (doCount) { colNames.resize (colNames.size() + 1, True); colNames[colNames.size() - 1] = "_COUNT_"; } if (printCommand && !showHelp) { if (!varName.empty()) { cout << varName << " = "; } cout << strc << endl; cout << " has been executed" << endl; } if (result.isTable()) { tabp = result.table(); if (printRows) { cout << " " << cmd << " result of " << tabp.nrow() << " rows" << endl; } if (printSelect && showResult && colNames.size() > 0) { if (printHeader) { // Show the selected column names. cout << colNames.nelements() << " selected columns: "; for (i=0; i= 0) { cout << endl; cout << " "; } cout << ' ' << command << endl; if (level >= 0) { cout << " " << tab.nrow() << " rows, " << tdesc.ncolumn() << " columns" << endl; } if (level > 0) { Vector colNames = tdesc.columnNames(); cout << " " << 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) { cout << " ndim=" << cdesc.ndim(); } if (! cdesc.shape().empty()) { cout << " shape=" << cdesc.shape(); } } if (cdesc.comment().empty()) { cout << " " << cdesc.comment(); } cout << endl; } } } } // Show the variable names and tables associated to them. void showTableMap (const TableMap& tables) { if (tables.empty()) { cout << " 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); } } } // Substitute possible table variables given like $var. String substituteName (const String& name, const TableMap& tables, vector& tabs) { TableMap::const_iterator fnd = tables.find(name); if (fnd == tables.end()) { return name; } tabs.push_back (&(fnd->second.first)); return String::toString (tabs.size()); } vector replaceVars (String& str, const TableMap& tables) { 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, tables, 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, tables, tabs); } str = out; return tabs; } // Ask and execute commands till quit or ^D is given. void askCommands (bool printCommand, bool printSelect, bool printMeas, bool printRows, bool printHeader, const String& delim, const String& prefix, const vector& commands) { #ifdef HAVE_READLINE string histFile; if (commands.empty()) { String homeDir = EnvironmentVariable::get("HOME"); if (! homeDir.empty()) { histFile = homeDir + "/.taql_history"; read_history(histFile.c_str()); } } #endif Regex varassRE("^[a-zA-Z_][a-zA-Z0-9_]*[ \t]*="); Regex assRE("[ \t]*="); Regex lwhiteRE("^[ \t]*"); Regex rwhiteRE("[ \t]*$"); TableMap tables; uInt inx=0; while (True) { try { String str; if (commands.empty()) { if (! readLineSkip (str, "TaQL> ")) { cerr << endl; break; } } else { if (inx >= commands.size()) { break; } str = commands[inx++]; } if (str == "h" || str == "-h" || str == "--help") { showHelp(); } else if (str == "?") { showTableMap (tables); } else if (str == "exit" || str == "quit" || str == "q") { break; } else { String varName; String::size_type assLen = varassRE.match (str.c_str(), str.size()); if (assLen != String::npos) { // Assignment to variable; get its name and remove from command. varName = str.before(assLen); str = str.from(assLen); varName.del (assRE); if (varName.empty()) { throw AipsError ("Variable name before =command is empty"); } } str.del (lwhiteRE); if (str.empty()) { // No command means that the variable will be removed. tables.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 = str.size(); while (sz > 0 && str[sz-1] == '?') { --sz; } Int level = str.size() - sz; String name = str.substr(0, sz); name.del (rwhiteRE); TableMap::const_iterator it = tables.find (name); if (it != tables.end()) { // It exists, so it must be a name. showTableInfo (name, it->second.first, it->second.second, level); } else { // No name, so it must be a command. String command(str); vector tabs = replaceVars (str, tables); Table tab = doCommand (printCommand, printSelect, printMeas, printRows, printHeader, delim, varName, prefix, str, tabs); if (!varName.empty() && !tab.isNull()) { // Keep the resulting table if a variable was given. tables[varName] = make_pair(tab, command); } } } } } catch (const TableParseError& x) { showParseError (x); } catch (const AipsError& x) { cerr << x.getMesg() << endl; } } #ifdef HAVE_READLINE if (! histFile.empty()) { write_history(histFile.c_str()); } #endif } vector fileCommands (const string& fname) { vector commands; bool appendLast = false; std::ifstream ifs(fname.c_str()); if (! ifs.good()) { throw AipsError("Cannot open file " + fname); } String line; getline (ifs, line); while (ifs.good()) { removeCR (line); vector parts = splitLine(line); for (size_t i=0; i 0 && style[0] == '-') { // no style value, thus ignore. style = string(); } else { // use style value. st++; } } } else if (arg == "-d") { if (st+1 < argc) { delim = argv[st+1]; st++; } } else if (arg == "-pc" || arg == "--printcommand") { printCommand = 1; } else if (arg == "-ps" || arg == "--printselect") { printSelect = 1; } else if (arg == "-pm" || arg == "--printmeasure") { printMeas = 1; } else if (arg == "-pr" || arg == "--printrows") { printRows = 1; } else if (arg == "-ph" || arg == "--printheader") { printHeader = 1; } else if (arg == "-nopc" || arg == "--noprintcommand") { printCommand = 0; } else if (arg == "-nops" || arg == "--noprintselect") { printSelect = 0; } else if (arg == "-nopm" || arg == "--noprintmeasure") { printMeas = 0; } else if (arg == "-nopr" || arg == "--noprintrows") { printRows = 0; } else if (arg == "-noph" || arg == "--noprintheader") { printHeader = 0;; } else if (arg == "-f") { if (st < argc-1) { st++; fname = argv[st]; } else { throw AipsError("No file name given after -f"); } } else if (arg == "-v" || arg == "--version") { showVersion(); return 0; } else if (arg == "-h" || arg == "--help") { showHelp(); return 0; } else if (arg[0] == '-') { cerr << arg << " is an invalid option" << endl; return 1; } else { break; } } string prefix; if (style.empty()) { style = "glish"; } prefix = "using style " + style + ' '; if (! fname.empty()) { vector commands = fileCommands (fname); if (! commands.empty()) { askCommands (printCommand!=0, printSelect!=0, printMeas!=0, printRows!=0, printHeader!=0, delim, prefix, commands); } } else if (st < argc) { // A command can be given as multiple parameters to make tab-completion // easier. Thus combine it all. String command(argv[st]); while (++st < argc) { command += ' ' + String(argv[st]); } // Execute the given command. doCommand (printCommand==1, printSelect==1, printMeas==1, printRows==1, printHeader==1, delim, String(), prefix, command, vector()); } else { // Ask the user for commands. cout << "Using default TaQL style " << style << endl; askCommands (printCommand!=0, printSelect!=0, printMeas!=0, printRows!=0, printHeader!=0, delim, prefix, vector()); } } catch (const TableParseError& x) { showParseError (x); } catch (const AipsError& x) { cerr << "\nCaught an exception: " << x.getMesg() << endl; return 1; } return 0; // successfully executed } casacore-2.4.1/tables/apps/tomf.cc000066400000000000000000000067351321422335000167750ustar00rootroot00000000000000//# tomf.cc: This program copies files to 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: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: showtable.cc 21480 2014-08-27 08:01:36Z gervandiepen $ #include #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 = new MultiHDF5 (outName, ByteIO::New, blockSize); } else { mfile = 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-2.4.1/tables/tables.dox000066400000000000000000000035401321422335000165330ustar00rootroot00000000000000//# 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: aips2-request@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. //
        }